mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-03-03 09:26:51 +00:00
Render user's actual workspace in ThemePicker preview (Part 3)
go/grid-migration-preview With this change, we can see actual grid migration in wallpaper preview. The approach here: we use a tmp table (favorites_preview) here specifically for this preview (to write off the migration results), and load from this tmp table workspace items if migration is necessary and successful. Otherwise, we load from the current workspace. UPDATED: this change should be completely compatible with the new multi-db grid migration algorithm. Here is why 1. In LauncherPreviewRender#renderScreenShot, I added a check to decide which grid migration preview method we should call. Once v2 preview method is implemented, it should be integrated with other parts of this change perfectly (the reason will be mentioned below). 2. While we have multiple DBs, mOpenHelper in LauncherProvider always points to the current db we are using. Queries using CONTENT_URI is routed to whatever DB mOpenHelper points to, so it works perfectly to directly operate on CONTENT_URI even when we use multi-db underneath the hood. 3. With 1 and 2 mentioned, I believe in order for this preview change to support multi-db, we only need to implement the V2 grid migration algorithm. Because most of what we are doing in this change is wrapped in GridSizeMigrationTask, it's perfectly safeguarded. Bug: 144052839 Change-Id: Ie6d6048d77326f96546c8a180a7cd8f15b47e4c4
This commit is contained in:
@@ -3,6 +3,7 @@ package com.android.launcher3.model;
|
||||
import static com.android.launcher3.LauncherSettings.Settings.EXTRA_VALUE;
|
||||
import static com.android.launcher3.Utilities.getPointString;
|
||||
import static com.android.launcher3.Utilities.parsePoint;
|
||||
import static com.android.launcher3.provider.LauncherDbUtils.copyTable;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.ContentValues;
|
||||
@@ -14,6 +15,7 @@ import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.graphics.Point;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
@@ -29,6 +31,7 @@ import com.android.launcher3.LauncherSettings.Settings;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.graphics.LauncherPreviewRenderer;
|
||||
import com.android.launcher3.pm.InstallSessionHelper;
|
||||
import com.android.launcher3.provider.LauncherDbUtils;
|
||||
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
|
||||
@@ -69,6 +72,7 @@ public class GridSizeMigrationTask {
|
||||
|
||||
private final SparseArray<ContentValues> mUpdateOperations = new SparseArray<>();
|
||||
private final HashSet<String> mValidPackages;
|
||||
private final String mTableName;
|
||||
|
||||
private final int mSrcX, mSrcY;
|
||||
private final int mTrgX, mTrgY;
|
||||
@@ -78,10 +82,12 @@ public class GridSizeMigrationTask {
|
||||
private final int mDestHotseatSize;
|
||||
|
||||
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
|
||||
HashSet<String> validPackages, Point sourceSize, Point targetSize) {
|
||||
HashSet<String> validPackages, boolean usePreviewTable, Point sourceSize,
|
||||
Point targetSize) {
|
||||
mContext = context;
|
||||
mDb = db;
|
||||
mValidPackages = validPackages;
|
||||
mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
|
||||
|
||||
mSrcX = sourceSize.x;
|
||||
mSrcY = sourceSize.y;
|
||||
@@ -97,10 +103,12 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
protected GridSizeMigrationTask(Context context, SQLiteDatabase db,
|
||||
HashSet<String> validPackages, int srcHotseatSize, int destHotseatSize) {
|
||||
HashSet<String> validPackages, boolean usePreviewTable, int srcHotseatSize,
|
||||
int destHotseatSize) {
|
||||
mContext = context;
|
||||
mDb = db;
|
||||
mValidPackages = validPackages;
|
||||
mTableName = usePreviewTable ? Favorites.PREVIEW_TABLE_NAME : Favorites.TABLE_NAME;
|
||||
|
||||
mSrcHotseatSize = srcHotseatSize;
|
||||
|
||||
@@ -120,7 +128,7 @@ public class GridSizeMigrationTask {
|
||||
// Update items
|
||||
int updateCount = mUpdateOperations.size();
|
||||
for (int i = 0; i < updateCount; i++) {
|
||||
mDb.update(Favorites.TABLE_NAME, mUpdateOperations.valueAt(i),
|
||||
mDb.update(mTableName, mUpdateOperations.valueAt(i),
|
||||
"_id=" + mUpdateOperations.keyAt(i), null);
|
||||
}
|
||||
|
||||
@@ -128,8 +136,8 @@ public class GridSizeMigrationTask {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "Removing items: " + mEntryToRemove.toConcatString());
|
||||
}
|
||||
mDb.delete(Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
|
||||
Favorites._ID, mEntryToRemove), null);
|
||||
mDb.delete(mTableName, Utilities.createDbSelectionQuery(Favorites._ID, mEntryToRemove),
|
||||
null);
|
||||
}
|
||||
|
||||
return updateCount > 0 || !mEntryToRemove.isEmpty();
|
||||
@@ -182,8 +190,8 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static IntArray getWorkspaceScreenIds(SQLiteDatabase db) {
|
||||
return LauncherDbUtils.queryIntArray(db, Favorites.TABLE_NAME, Favorites.SCREEN,
|
||||
static IntArray getWorkspaceScreenIds(SQLiteDatabase db, String tableName) {
|
||||
return LauncherDbUtils.queryIntArray(db, tableName, Favorites.SCREEN,
|
||||
Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP,
|
||||
Favorites.SCREEN, Favorites.SCREEN);
|
||||
}
|
||||
@@ -192,7 +200,7 @@ public class GridSizeMigrationTask {
|
||||
* @return true if any DB change was made
|
||||
*/
|
||||
protected boolean migrateWorkspace() throws Exception {
|
||||
IntArray allScreens = getWorkspaceScreenIds(mDb);
|
||||
IntArray allScreens = getWorkspaceScreenIds(mDb, mTableName);
|
||||
if (allScreens.isEmpty()) {
|
||||
throw new Exception("Unable to get workspace screens");
|
||||
}
|
||||
@@ -244,12 +252,12 @@ public class GridSizeMigrationTask {
|
||||
/**
|
||||
* 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).
|
||||
* 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.
|
||||
@@ -362,9 +370,9 @@ public class GridSizeMigrationTask {
|
||||
/**
|
||||
* Tries the remove the provided row and column.
|
||||
*
|
||||
* @param items all the items on the screen under operation
|
||||
* @param items all the items on the screen under operation
|
||||
* @param outLoss array of size 2. The first entry is filled with weight loss, and the second
|
||||
* with the overall item movement.
|
||||
* with the overall item movement.
|
||||
*/
|
||||
private ArrayList<DbEntry> tryRemove(int col, int row, int startY,
|
||||
ArrayList<DbEntry> items, float[] outLoss) {
|
||||
@@ -379,13 +387,13 @@ public class GridSizeMigrationTask {
|
||||
|
||||
for (DbEntry item : items) {
|
||||
if ((item.cellX <= col && (item.spanX + item.cellX) > col)
|
||||
|| (item.cellY <= row && (item.spanY + item.cellY) > row)) {
|
||||
|| (item.cellY <= row && (item.spanY + item.cellY) > row)) {
|
||||
removedItems.add(item);
|
||||
if (item.cellX >= col) item.cellX --;
|
||||
if (item.cellY >= row) item.cellY --;
|
||||
if (item.cellX >= col) item.cellX--;
|
||||
if (item.cellY >= row) item.cellY--;
|
||||
} else {
|
||||
if (item.cellX > col) item.cellX --;
|
||||
if (item.cellY > row) item.cellY --;
|
||||
if (item.cellX > col) item.cellX--;
|
||||
if (item.cellY > row) item.cellY--;
|
||||
finalItems.add(item);
|
||||
occupied.markCells(item, true);
|
||||
}
|
||||
@@ -438,9 +446,9 @@ public class GridSizeMigrationTask {
|
||||
/**
|
||||
* Recursively finds a placement for the provided items.
|
||||
*
|
||||
* @param index the position in {@link #itemsToPlace} to start looking at.
|
||||
* @param weightLoss total weight loss upto this point
|
||||
* @param moveCost total move cost upto this point
|
||||
* @param index the position in {@link #itemsToPlace} to start looking at.
|
||||
* @param weightLoss total weight loss upto this point
|
||||
* @param moveCost total move cost upto this point
|
||||
* @param itemsPlaced all the items already placed upto this point
|
||||
*/
|
||||
public void find(int index, float weightLoss, float moveCost,
|
||||
@@ -481,11 +489,11 @@ public class GridSizeMigrationTask {
|
||||
float newMoveCost = moveCost;
|
||||
if (x != myX) {
|
||||
me.cellX = x;
|
||||
newMoveCost ++;
|
||||
newMoveCost++;
|
||||
}
|
||||
if (y != myY) {
|
||||
me.cellY = y;
|
||||
newMoveCost ++;
|
||||
newMoveCost++;
|
||||
}
|
||||
if (ignoreMove) {
|
||||
newMoveCost = moveCost;
|
||||
@@ -500,35 +508,35 @@ public class GridSizeMigrationTask {
|
||||
|
||||
// Try resizing horizontally
|
||||
if (myW > me.minSpanX && occupied.isRegionVacant(x, y, myW - 1, myH)) {
|
||||
me.spanX --;
|
||||
me.spanX--;
|
||||
occupied.markCells(me, true);
|
||||
// 1 extra move cost
|
||||
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
||||
occupied.markCells(me, false);
|
||||
me.spanX ++;
|
||||
me.spanX++;
|
||||
}
|
||||
|
||||
// Try resizing vertically
|
||||
if (myH > me.minSpanY && occupied.isRegionVacant(x, y, myW, myH - 1)) {
|
||||
me.spanY --;
|
||||
me.spanY--;
|
||||
occupied.markCells(me, true);
|
||||
// 1 extra move cost
|
||||
find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
|
||||
occupied.markCells(me, false);
|
||||
me.spanY ++;
|
||||
me.spanY++;
|
||||
}
|
||||
|
||||
// Try resizing horizontally & vertically
|
||||
if (myH > me.minSpanY && myW > me.minSpanX &&
|
||||
occupied.isRegionVacant(x, y, myW - 1, myH - 1)) {
|
||||
me.spanX --;
|
||||
me.spanY --;
|
||||
me.spanX--;
|
||||
me.spanY--;
|
||||
occupied.markCells(me, true);
|
||||
// 2 extra move cost
|
||||
find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
|
||||
occupied.markCells(me, false);
|
||||
me.spanX ++;
|
||||
me.spanY ++;
|
||||
me.spanX++;
|
||||
me.spanY++;
|
||||
}
|
||||
me.cellX = myX;
|
||||
me.cellY = myY;
|
||||
@@ -565,11 +573,11 @@ public class GridSizeMigrationTask {
|
||||
float newMoveCost = moveCost;
|
||||
if (newX != myX) {
|
||||
me.cellX = newX;
|
||||
newMoveCost ++;
|
||||
newMoveCost++;
|
||||
}
|
||||
if (newY != myY) {
|
||||
me.cellY = newY;
|
||||
newMoveCost ++;
|
||||
newMoveCost++;
|
||||
}
|
||||
if (ignoreMove) {
|
||||
newMoveCost = moveCost;
|
||||
@@ -602,7 +610,7 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
private ArrayList<DbEntry> loadHotseatEntries() {
|
||||
Cursor c = queryWorkspace(
|
||||
Cursor c = queryWorkspace(
|
||||
new String[]{
|
||||
Favorites._ID, // 0
|
||||
Favorites.ITEM_TYPE, // 1
|
||||
@@ -787,7 +795,7 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
protected Cursor queryWorkspace(String[] columns, String where) {
|
||||
return mDb.query(Favorites.TABLE_NAME, columns, where, null, null, null, null);
|
||||
return mDb.query(mTableName, columns, where, null, null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -879,24 +887,44 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrates the workspace and hotseat in case their sizes changed.
|
||||
* Check given a new IDP, if migration is necessary.
|
||||
*/
|
||||
public static boolean needsToMigrate(Context context, InvariantDeviceProfile idp) {
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
|
||||
|
||||
return !gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, ""))
|
||||
|| idp.numHotseatIcons != prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
|
||||
idp.numHotseatIcons);
|
||||
}
|
||||
|
||||
/** See {@link #migrateGridIfNeeded(Context, InvariantDeviceProfile)} */
|
||||
public static boolean migrateGridIfNeeded(Context context) {
|
||||
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
|
||||
return true;
|
||||
}
|
||||
return migrateGridIfNeeded(context, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the migration algorithm if needed. For preview, we provide the intended idp because it
|
||||
* has not been changed. If idp is null, we read it from the context, for actual grid migration.
|
||||
*
|
||||
* @return false if the migration failed.
|
||||
*/
|
||||
public static boolean migrateGridIfNeeded(Context context) {
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
InvariantDeviceProfile idp = LauncherAppState.getIDP(context);
|
||||
public static boolean migrateGridIfNeeded(Context context, InvariantDeviceProfile idp) {
|
||||
boolean migrateForPreview = idp != null;
|
||||
if (!migrateForPreview) {
|
||||
idp = LauncherAppState.getIDP(context);
|
||||
}
|
||||
|
||||
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
|
||||
|
||||
if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
|
||||
idp.numHotseatIcons == prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT,
|
||||
idp.numHotseatIcons)) {
|
||||
// Skip if workspace and hotseat sizes have not changed.
|
||||
if (!needsToMigrate(context, idp)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
long migrationStartTime = System.currentTimeMillis();
|
||||
SharedPreferences prefs = Utilities.getPrefs(context);
|
||||
String gridSizeString = getPointString(idp.numColumns, idp.numRows);
|
||||
long migrationStartTime = SystemClock.elapsedRealtime();
|
||||
try (SQLiteTransaction transaction = (SQLiteTransaction) Settings.call(
|
||||
context.getContentResolver(), Settings.METHOD_NEW_TRANSACTION)
|
||||
.getBinder(Settings.EXTRA_VALUE)) {
|
||||
@@ -907,33 +935,39 @@ public class GridSizeMigrationTask {
|
||||
KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
|
||||
|
||||
boolean dbChanged = false;
|
||||
if (migrateForPreview) {
|
||||
copyTable(transaction.getDb(), Favorites.TABLE_NAME, Favorites.PREVIEW_TABLE_NAME,
|
||||
context);
|
||||
}
|
||||
|
||||
GridBackupTable backupTable = new GridBackupTable(context, transaction.getDb(),
|
||||
srcHotseatCount, sourceSize.x, sourceSize.y);
|
||||
if (backupTable.backupOrRestoreAsNeeded()) {
|
||||
if (migrateForPreview ? backupTable.restoreToPreviewIfBackupExists()
|
||||
: backupTable.backupOrRestoreAsNeeded()) {
|
||||
dbChanged = true;
|
||||
srcHotseatCount = backupTable.getRestoreHotseatAndGridSize(sourceSize);
|
||||
}
|
||||
|
||||
HashSet<String> validPackages = getValidPackages(context);
|
||||
// Hotseat
|
||||
// Hotseat.
|
||||
if (srcHotseatCount != idp.numHotseatIcons) {
|
||||
// Migrate hotseat.
|
||||
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(),
|
||||
validPackages, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
|
||||
dbChanged = new GridSizeMigrationTask(context, transaction.getDb(), validPackages,
|
||||
migrateForPreview, srcHotseatCount, idp.numHotseatIcons).migrateHotseat();
|
||||
}
|
||||
|
||||
// Grid size
|
||||
Point targetSize = new Point(idp.numColumns, idp.numRows);
|
||||
if (new MultiStepMigrationTask(validPackages, context, transaction.getDb())
|
||||
.migrate(sourceSize, targetSize)) {
|
||||
if (new MultiStepMigrationTask(validPackages, context, transaction.getDb(),
|
||||
migrateForPreview).migrate(sourceSize, targetSize)) {
|
||||
dbChanged = true;
|
||||
}
|
||||
|
||||
if (dbChanged) {
|
||||
// Make sure we haven't removed everything.
|
||||
final Cursor c = context.getContentResolver().query(
|
||||
Favorites.CONTENT_URI, null, null, null, null);
|
||||
migrateForPreview ? Favorites.PREVIEW_CONTENT_URI : Favorites.CONTENT_URI,
|
||||
null, null, null, null);
|
||||
boolean hasData = c.moveToNext();
|
||||
c.close();
|
||||
if (!hasData) {
|
||||
@@ -942,21 +976,25 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
transaction.commit();
|
||||
Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
|
||||
if (!migrateForPreview) {
|
||||
Settings.call(context.getContentResolver(), Settings.METHOD_REFRESH_BACKUP_TABLE);
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error during grid migration", e);
|
||||
Log.e(TAG, "Error during preview grid migration", e);
|
||||
|
||||
return false;
|
||||
} finally {
|
||||
Log.v(TAG, "Workspace migration completed in "
|
||||
+ (System.currentTimeMillis() - migrationStartTime));
|
||||
Log.v(TAG, "Preview workspace migration completed in "
|
||||
+ (SystemClock.elapsedRealtime() - migrationStartTime));
|
||||
|
||||
// Save current configuration, so that the migration does not run again.
|
||||
prefs.edit()
|
||||
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
|
||||
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
|
||||
.apply();
|
||||
if (!migrateForPreview) {
|
||||
// Save current configuration, so that the migration does not run again.
|
||||
prefs.edit()
|
||||
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
|
||||
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
|
||||
.apply();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -989,7 +1027,7 @@ public class GridSizeMigrationTask {
|
||||
.getBinder(Settings.EXTRA_VALUE)) {
|
||||
GridSizeMigrationTask task = new GridSizeMigrationTask(
|
||||
context, transaction.getDb(), getValidPackages(context),
|
||||
Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
false /* usePreviewTable */, Integer.MAX_VALUE, Integer.MAX_VALUE);
|
||||
|
||||
// Load all the valid entries
|
||||
ArrayList<DbEntry> items = task.loadHotseatEntries();
|
||||
@@ -1011,12 +1049,14 @@ public class GridSizeMigrationTask {
|
||||
private final HashSet<String> mValidPackages;
|
||||
private final Context mContext;
|
||||
private final SQLiteDatabase mDb;
|
||||
private final boolean mUsePreviewTable;
|
||||
|
||||
public MultiStepMigrationTask(HashSet<String> validPackages, Context context,
|
||||
SQLiteDatabase db) {
|
||||
SQLiteDatabase db, boolean usePreviewTable) {
|
||||
mValidPackages = validPackages;
|
||||
mContext = context;
|
||||
mDb = db;
|
||||
mUsePreviewTable = usePreviewTable;
|
||||
}
|
||||
|
||||
public boolean migrate(Point sourceSize, Point targetSize) throws Exception {
|
||||
@@ -1052,8 +1092,8 @@ public class GridSizeMigrationTask {
|
||||
}
|
||||
|
||||
protected boolean runStepTask(Point sourceSize, Point nextSize) throws Exception {
|
||||
return new GridSizeMigrationTask(mContext, mDb,
|
||||
mValidPackages, sourceSize, nextSize).migrateWorkspace();
|
||||
return new GridSizeMigrationTask(mContext, mDb, mValidPackages, mUsePreviewTable,
|
||||
sourceSize, nextSize).migrateWorkspace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user