use of com.android.launcher3.util.IntSparseArrayMap in project android_packages_apps_Trebuchet by LineageOS.
the class AddWorkspaceItemsTaskTest method setup.
@Before
public void setup() {
mModelHelper = new LauncherModelHelper();
mTargetContext = RuntimeEnvironment.application;
mIdp = InvariantDeviceProfile.INSTANCE.get(mTargetContext);
mIdp.numColumns = mIdp.numRows = 5;
mAppState = LauncherAppState.getInstance(mTargetContext);
mExistingScreens = new IntArray();
mScreenOccupancy = new IntSparseArrayMap<>();
mNewScreens = new IntArray();
}
use of com.android.launcher3.util.IntSparseArrayMap in project android_packages_apps_Trebuchet by LineageOS.
the class PackageUpdatedTask method execute.
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList appsList) {
final Context context = app.getContext();
final IconCache iconCache = app.getIconCache();
final String[] packages = mPackages;
final int N = packages.length;
FlagOp flagOp = FlagOp.NO_OP;
final HashSet<String> packageSet = new HashSet<>(Arrays.asList(packages));
ItemInfoMatcher matcher = ItemInfoMatcher.ofPackages(packageSet, mUser);
final HashSet<ComponentName> removedComponents = new HashSet<>();
switch(mOp) {
case OP_ADD:
{
for (int i = 0; i < N; i++) {
if (DEBUG)
Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
appsList.removePackage(packages[i], mUser);
}
appsList.addPackage(context, packages[i], mUser);
// Automatically add homescreen icon for work profile apps for below O device.
if (!Utilities.ATLEAST_OREO && !Process.myUserHandle().equals(mUser)) {
SessionCommitReceiver.queueAppIconAddition(context, packages[i], mUser);
}
}
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
}
case OP_UPDATE:
try (SafeCloseable t = appsList.trackRemoves(a -> removedComponents.add(a.componentName))) {
for (int i = 0; i < N; i++) {
if (DEBUG)
Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
iconCache.updateIconsForPkg(packages[i], mUser);
appsList.updatePackage(context, packages[i], mUser);
app.getWidgetCache().removePackage(packages[i], mUser);
}
}
// Since package was just updated, the target must be available now.
flagOp = FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_REMOVE:
{
for (int i = 0; i < N; i++) {
FileLog.d(TAG, "Removing app icon" + packages[i]);
iconCache.removeIconsForPkg(packages[i], mUser);
}
// Fall through
}
case OP_UNAVAILABLE:
for (int i = 0; i < N; i++) {
if (DEBUG)
Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
appsList.removePackage(packages[i], mUser);
app.getWidgetCache().removePackage(packages[i], mUser);
}
flagOp = FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_SUSPEND:
case OP_UNSUSPEND:
flagOp = mOp == OP_SUSPEND ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED) : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_SUSPENDED);
if (DEBUG)
Log.d(TAG, "mAllAppsList.(un)suspend " + N);
appsList.updateDisabledFlags(matcher, flagOp);
break;
case OP_USER_AVAILABILITY_CHANGE:
{
UserManagerState ums = new UserManagerState();
ums.init(UserCache.INSTANCE.get(context), context.getSystemService(UserManager.class));
flagOp = ums.isUserQuiet(mUser) ? FlagOp.addFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER) : FlagOp.removeFlag(WorkspaceItemInfo.FLAG_DISABLED_QUIET_USER);
// We want to update all packages for this user.
matcher = ItemInfoMatcher.ofUser(mUser);
appsList.updateDisabledFlags(matcher, flagOp);
// We are not synchronizing here, as int operations are atomic
appsList.setFlags(FLAG_QUIET_MODE_ENABLED, ums.isAnyProfileQuietModeEnabled());
break;
}
}
bindApplicationsIfNeeded();
final IntSparseArrayMap<Boolean> removedShortcuts = new IntSparseArrayMap<>();
// Update shortcut infos
if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
final ArrayList<WorkspaceItemInfo> updatedWorkspaceItems = new ArrayList<>();
final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<>();
// For system apps, package manager send OP_UPDATE when an app is enabled.
final boolean isNewApkAvailable = mOp == OP_ADD || mOp == OP_UPDATE;
synchronized (dataModel) {
for (ItemInfo info : dataModel.itemsIdMap) {
if (info instanceof WorkspaceItemInfo && mUser.equals(info.user)) {
WorkspaceItemInfo si = (WorkspaceItemInfo) info;
boolean infoUpdated = false;
boolean shortcutUpdated = false;
// Update shortcuts which use iconResource.
if ((si.iconResource != null) && packageSet.contains(si.iconResource.packageName)) {
LauncherIcons li = LauncherIcons.obtain(context);
BitmapInfo iconInfo = li.createIconBitmap(si.iconResource);
li.recycle();
if (iconInfo != null) {
si.bitmap = iconInfo;
infoUpdated = true;
}
}
ComponentName cn = si.getTargetComponent();
if (cn != null && matcher.matches(si, cn)) {
String packageName = cn.getPackageName();
if (si.hasStatusFlag(WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI)) {
removedShortcuts.put(si.id, false);
if (mOp == OP_REMOVE) {
continue;
}
}
if (si.isPromise() && isNewApkAvailable) {
boolean isTargetValid = true;
if (si.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
List<ShortcutInfo> shortcut = new ShortcutRequest(context, mUser).forPackage(cn.getPackageName(), si.getDeepShortcutId()).query(ShortcutRequest.PINNED);
if (shortcut.isEmpty()) {
isTargetValid = false;
} else {
si.updateFromDeepShortcutInfo(shortcut.get(0), context);
infoUpdated = true;
}
} else if (!cn.getClassName().equals(IconCache.EMPTY_CLASS_NAME)) {
isTargetValid = context.getSystemService(LauncherApps.class).isActivityEnabled(cn, mUser);
}
if (si.hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINSTALL_ICON)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
} else if (si.hasPromiseIconUi()) {
removedShortcuts.put(si.id, true);
continue;
}
} else if (!isTargetValid) {
removedShortcuts.put(si.id, true);
FileLog.e(TAG, "Restored shortcut no longer valid " + si.getIntent());
continue;
} else {
si.status = WorkspaceItemInfo.DEFAULT;
infoUpdated = true;
}
} else if (isNewApkAvailable && removedComponents.contains(cn)) {
if (updateWorkspaceItemIntent(context, si, packageName)) {
infoUpdated = true;
}
}
if (isNewApkAvailable && si.itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(si, si.usingLowResIcon());
infoUpdated = true;
}
int oldRuntimeFlags = si.runtimeStatusFlags;
si.runtimeStatusFlags = flagOp.apply(si.runtimeStatusFlags);
if (si.runtimeStatusFlags != oldRuntimeFlags) {
shortcutUpdated = true;
}
}
if (infoUpdated || shortcutUpdated) {
updatedWorkspaceItems.add(si);
}
if (infoUpdated) {
getModelWriter().updateItemInDatabase(si);
}
} else if (info instanceof LauncherAppWidgetInfo && isNewApkAvailable) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
if (mUser.equals(widgetInfo.user) && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && packageSet.contains(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
// adding this flag ensures that launcher shows 'click to setup'
// if the widget has a config activity. In case there is no config
// activity, it will be marked as 'restored' during bind.
widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
widgets.add(widgetInfo);
getModelWriter().updateItemInDatabase(widgetInfo);
}
}
}
}
bindUpdatedWorkspaceItems(updatedWorkspaceItems);
if (!removedShortcuts.isEmpty()) {
deleteAndBindComponentsRemoved(ItemInfoMatcher.ofItemIds(removedShortcuts, false));
}
if (!widgets.isEmpty()) {
scheduleCallbackTask(c -> c.bindWidgetsRestored(widgets));
}
}
final HashSet<String> removedPackages = new HashSet<>();
if (mOp == OP_REMOVE) {
// Mark all packages in the broadcast to be removed
Collections.addAll(removedPackages, packages);
// No need to update the removedComponents as
// removedPackages is a super-set of removedComponents
} else if (mOp == OP_UPDATE) {
// Mark disabled packages in the broadcast to be removed
final LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
for (int i = 0; i < N; i++) {
if (!launcherApps.isPackageEnabled(packages[i], mUser)) {
removedPackages.add(packages[i]);
}
}
}
if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
ItemInfoMatcher removeMatch = ItemInfoMatcher.ofPackages(removedPackages, mUser).or(ItemInfoMatcher.ofComponents(removedComponents, mUser)).and(ItemInfoMatcher.ofItemIds(removedShortcuts, true));
deleteAndBindComponentsRemoved(removeMatch);
// Remove any queued items from the install queue
InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
}
if (Utilities.ATLEAST_OREO && mOp == OP_ADD) {
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < N; i++) {
dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
}
bindUpdatedWidgets(dataModel);
}
}
use of com.android.launcher3.util.IntSparseArrayMap in project android_packages_apps_Trebuchet by LineageOS.
the class GridSizeMigrationTask method removeBrokenHotseatItems.
/**
* Removes any broken item from the hotseat.
*
* @return a map with occupied hotseat position set to non-null value.
*/
public static IntSparseArrayMap<Object> removeBrokenHotseatItems(Context context) throws Exception {
try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION).getBinder(Settings.EXTRA_VALUE)) {
GridSizeMigrationTask task = new GridSizeMigrationTask(context, transaction.getDb(), getValidPackages(context), false, /* usePreviewTable */
Integer.MAX_VALUE, Integer.MAX_VALUE);
// Load all the valid entries
ArrayList<DbEntry> items = task.loadHotseatEntries();
// Delete any entry marked for deletion by above load.
task.applyOperations();
IntSparseArrayMap<Object> positions = new IntSparseArrayMap<>();
for (DbEntry item : items) {
positions.put(item.screenId, item);
}
transaction.commit();
return positions;
}
}
use of com.android.launcher3.util.IntSparseArrayMap in project android_packages_apps_Trebuchet by LineageOS.
the class GridSizeMigrationTask method migrateScreen.
/**
* Migrate a particular screen id.
* Strategy:
* 1) For all possible combinations of row and column, pick the one which causes the least
* data loss: {@link #tryRemove(int, int, int, ArrayList, float[])}
* 2) Maintain a list of all lost items before this screen, and add any new item lost from
* this screen to that list as well.
* 3) If all those items from the above list can be placed on this screen, place them
* (otherwise they are placed on a new screen).
*/
protected void migrateScreen(int screenId) {
// If we are migrating the first screen, do not touch the first row.
int startY = (FeatureFlags.QSB_ON_FIRST_SCREEN && screenId == Workspace.FIRST_SCREEN_ID) ? 1 : 0;
ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
int removedCol = Integer.MAX_VALUE;
int removedRow = Integer.MAX_VALUE;
// removeWt represents the cost function for loss of items during migration, and moveWt
// represents the cost function for repositioning the items. moveWt is only considered if
// removeWt is same for two different configurations.
// Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
// cost.
float removeWt = Float.MAX_VALUE;
float moveWt = Float.MAX_VALUE;
float[] outLoss = new float[2];
ArrayList<DbEntry> finalItems = null;
// Try removing all possible combinations
for (int x = 0; x < mSrcX; x++) {
// nicely aligned with hotseat.
for (int y = mSrcY - 1; y >= startY; y--) {
// Use a deep copy when trying out a particular combination as it can change
// the underlying object.
ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss);
if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
removeWt = outLoss[0];
moveWt = outLoss[1];
removedCol = mShouldRemoveX ? x : removedCol;
removedRow = mShouldRemoveY ? y : removedRow;
finalItems = itemsOnScreen;
}
// No need to loop over all rows, if a row removal is not needed.
if (!mShouldRemoveY) {
break;
}
}
if (!mShouldRemoveX) {
break;
}
}
if (DEBUG) {
Log.d(TAG, String.format("Removing row %d, column %d on screen %d", removedRow, removedCol, screenId));
}
IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
for (DbEntry e : deepCopy(items)) {
itemMap.put(e.id, e);
}
for (DbEntry item : finalItems) {
DbEntry org = itemMap.get(item.id);
itemMap.remove(item.id);
// Check if update is required
if (!item.columnsSame(org)) {
update(item);
}
}
// The remaining items in {@link #itemMap} are those which didn't get placed.
for (DbEntry item : itemMap) {
mCarryOver.add(item);
}
if (!mCarryOver.isEmpty() && removeWt == 0) {
// No new items were removed in this step. Try placing all the items on this screen.
GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY);
occupied.markCells(0, 0, mTrgX, startY, true);
for (DbEntry item : finalItems) {
occupied.markCells(item, true);
}
OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, deepCopy(mCarryOver), startY, true);
placement.find();
if (placement.lowestWeightLoss == 0) {
for (DbEntry item : placement.finalPlacedItems) {
item.screenId = screenId;
update(item);
}
mCarryOver.clear();
}
}
}
use of com.android.launcher3.util.IntSparseArrayMap in project android_packages_apps_Trebuchet by LineageOS.
the class GridSizeMigrationTask method migrateWorkspace.
/**
* @return true if any DB change was made
*/
protected boolean migrateWorkspace() throws Exception {
IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
if (allScreens.isEmpty()) {
throw new Exception("Unable to get workspace screens");
}
for (int i = 0; i < allScreens.size(); i++) {
int screenId = allScreens.get(i);
if (DEBUG) {
Log.d(TAG, "Migrating " + screenId);
}
migrateScreen(screenId);
}
if (!mCarryOver.isEmpty()) {
IntSparseArrayMap<DbEntry> itemMap = new IntSparseArrayMap<>();
for (DbEntry e : mCarryOver) {
itemMap.put(e.id, e);
}
do {
// Some items are still remaining. Try adding a few new screens.
// At every iteration, make sure that at least one item is removed from
// {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
// break the loop and abort migration by throwing an exception.
OptimalPlacementSolution placement = new OptimalPlacementSolution(new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true);
placement.find();
if (placement.finalPlacedItems.size() > 0) {
int newScreenId = LauncherSettings.Settings.call(mContext.getContentResolver(), LauncherSettings.Settings.METHOD_NEW_SCREEN_ID).getInt(EXTRA_VALUE);
for (DbEntry item : placement.finalPlacedItems) {
if (!mCarryOver.remove(itemMap.get(item.id))) {
throw new Exception("Unable to find matching items");
}
item.screenId = newScreenId;
update(item);
}
} else {
throw new Exception("None of the items can be placed on an empty screen");
}
} while (!mCarryOver.isEmpty());
}
return applyOperations();
}
Aggregations