Support grid preview with v2 migration algorithm

The focus of ag/10346770 is around the actual algorithm, while in the meantime our preview logic has changed during the code review of ag/10100264.

GridSizeMigrationTaskV2 addresses both cases, the difference being preview passes in constructed IDP while actual migration uses IDP from the current Context.

When doing actual migration, we call METHOD_UPDATE_CURRENT_OPEN_HELPER to update the current db helper and copy the favorites table from the previous db into the current db in favorites_tmp table. Then we do migration from there.

When calculating preview, I added METHOD_PREP_FOR_PREVIEW in this change to copy the favorites table from the intended grid setting to the current grid setting in favorites_preview table. Then we calculate migration from the current favorites table to favorites_preview table and save into favorites_preview table.

Bug: 144052802
Fixes: 144052839

Test: Manual

Change-Id: I64a8b61a4e0bf8399c0ae1af4ef9d2bde0f1ee2f
This commit is contained in:
Tracy Zhou 2020-03-17 18:28:38 -07:00
parent ae581cdfdd
commit c0000450b5
5 changed files with 117 additions and 51 deletions

View File

@ -85,6 +85,7 @@ import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.function.Supplier;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@ -145,7 +146,7 @@ public class LauncherProvider extends ContentProvider {
*/
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
mOpenHelper = new DatabaseHelper(getContext());
mOpenHelper = DatabaseHelper.createDatabaseHelper(getContext());
if (RestoreDbTask.isPending(getContext())) {
if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
@ -159,17 +160,17 @@ public class LauncherProvider extends ContentProvider {
}
}
private synchronized boolean updateCurrentOpenHelper() {
final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
private synchronized boolean prepForMigration(String dbFile, String targetTableName,
Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
return false;
}
DatabaseHelper oldHelper = mOpenHelper;
mOpenHelper = new DatabaseHelper(getContext());
copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
oldHelper.close();
final DatabaseHelper helper = src.get();
mOpenHelper = dst.get();
copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
mOpenHelper.getWritableDatabase(), targetTableName, getContext());
helper.close();
return true;
}
@ -425,7 +426,23 @@ public class LauncherProvider extends ContentProvider {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
updateCurrentOpenHelper());
prepForMigration(
InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
Favorites.TMP_TABLE,
() -> mOpenHelper,
() -> DatabaseHelper.createDatabaseHelper(getContext())));
return result;
}
}
case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
prepForMigration(
arg /* dbFile */,
Favorites.PREVIEW_TABLE_NAME,
() -> DatabaseHelper.createDatabaseHelper(getContext(), arg),
() -> mOpenHelper));
return result;
}
}
@ -596,23 +613,31 @@ public class LauncherProvider extends ContentProvider {
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
DatabaseHelper(Context context) {
this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
context).dbFile : LauncherFiles.LAUNCHER_DB);
static DatabaseHelper createDatabaseHelper(Context context) {
return createDatabaseHelper(context, null);
}
static DatabaseHelper createDatabaseHelper(Context context, String dbName) {
if (dbName == null) {
dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
context).dbFile : LauncherFiles.LAUNCHER_DB;
}
DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
if (!tableExists(getReadableDatabase(), Favorites.TABLE_NAME)) {
if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
// This operation is a no-op if the table already exists.
addFavoritesTable(getWritableDatabase(), true);
databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
}
if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
mBackupTableExists = tableExists(getReadableDatabase(),
Favorites.BACKUP_TABLE_NAME);
databaseHelper.mBackupTableExists = tableExists(
databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
}
initIds();
databaseHelper.initIds();
return databaseHelper;
}
/**

View File

@ -326,10 +326,16 @@ public class LauncherSettings {
public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
return cr.call(CONTENT_URI, method, null, null);
return call(cr, method, null);
}
public static Bundle call(ContentResolver cr, String method, String arg) {
return cr.call(CONTENT_URI, method, arg, null);
}
}
}

View File

@ -21,7 +21,6 @@ import static android.view.View.VISIBLE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.GridSizeMigrationTask.needsToMigrate;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@ -397,7 +396,10 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private void populate() {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
boolean needsToMigrate = needsToMigrate(mContext, mIdp);
boolean needsToMigrate =
MULTI_DB_GRID_MIRATION_ALGO.get()
? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)
: GridSizeMigrationTask.needsToMigrate(mContext, mIdp);
boolean success = false;
if (needsToMigrate) {
success = MULTI_DB_GRID_MIRATION_ALGO.get()

View File

@ -123,8 +123,16 @@ public class GridSizeMigrationTaskV2 {
}
/**
* 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.
* When migrating the grid for preview, we copy the table
* {@link LauncherSettings.Favorites.TABLE_NAME} into
* {@link LauncherSettings.Favorites.PREVIEW_TABLE_NAME}, run grid size migration from the
* former to the later, then use the later table for preview.
*
* Similarly when doing the actual grid migration, the former grid option's table
* {@link LauncherSettings.Favorites.TABLE_NAME} is copied into the new grid option's
* {@link LauncherSettings.Favorites.TMP_TABLE}, we then run the grid size migration algorithm
* to migrate the later to the former, and load the workspace from the default
* {@link LauncherSettings.Favorites.TABLE_NAME}.
*
* @return false if the migration failed.
*/
@ -151,7 +159,14 @@ public class GridSizeMigrationTaskV2 {
HashSet<String> validPackages = getValidPackages(context);
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
if (!LauncherSettings.Settings.call(
if (migrateForPreview) {
if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW, idp.dbFile).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
return false;
}
} else if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
@ -164,9 +179,13 @@ public class GridSizeMigrationTaskV2 {
LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
LauncherSettings.Settings.EXTRA_VALUE)) {
DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
DbReader srcReader = new DbReader(t.getDb(),
migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
: LauncherSettings.Favorites.TMP_TABLE,
context, validPackages, srcHotseatCount);
DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
DbReader destReader = new DbReader(t.getDb(),
migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
: LauncherSettings.Favorites.TABLE_NAME,
context, validPackages, idp.numHotseatIcons);
Point targetSize = new Point(idp.numColumns, idp.numRows);
@ -174,7 +193,9 @@ public class GridSizeMigrationTaskV2 {
srcReader, destReader, idp.numHotseatIcons, targetSize);
task.migrate();
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
if (!migrateForPreview) {
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
}
t.commit();
return true;
@ -186,11 +207,13 @@ public class GridSizeMigrationTaskV2 {
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - 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();
}
}
}
@ -202,7 +225,7 @@ public class GridSizeMigrationTaskV2 {
// Migrate hotseat
HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
hotseatSolution.find();
// Sort the items by the reading order.
@ -215,7 +238,7 @@ public class GridSizeMigrationTaskV2 {
}
List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
mDestReader, mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
workspaceSolution.find();
if (mWorkspaceDiff.isEmpty()) {
break;
@ -225,7 +248,8 @@ public class GridSizeMigrationTaskV2 {
int screenId = mDestReader.mLastScreenId + 1;
while (!mWorkspaceDiff.isEmpty()) {
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
mDestReader, mContext, new ArrayList<>(), screenId, mTrgX, mTrgY,
mWorkspaceDiff);
workspaceSolution.find();
screenId++;
}
@ -246,7 +270,8 @@ public class GridSizeMigrationTaskV2 {
}
private static void insertEntryInDb(SQLiteDatabase db, Context context,
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry, String srcTableName,
String destTableName) {
int id = -1;
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@ -283,8 +308,8 @@ public class GridSizeMigrationTaskV2 {
return;
}
Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
Cursor c = db.query(srcTableName, null, LauncherSettings.Favorites._ID + " = '" + id + "'",
null, null, null, null);
while (c.moveToNext()) {
ContentValues values = new ContentValues();
@ -294,14 +319,14 @@ public class GridSizeMigrationTaskV2 {
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
LauncherSettings.Settings.EXTRA_VALUE));
db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
db.insert(destTableName, null, values);
}
c.close();
}
private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, entryId), null);
private static void removeEntryFromDb(SQLiteDatabase db, String tableName, IntArray entryId) {
db.delete(tableName,
Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryId), null);
}
private static HashSet<String> getValidPackages(Context context) {
@ -325,6 +350,7 @@ public class GridSizeMigrationTaskV2 {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final DbReader mDestReader;
private final Context mContext;
private final GridOccupancy mOccupied;
private final int mScreenId;
@ -335,11 +361,12 @@ public class GridSizeMigrationTaskV2 {
private int mNextStartX;
private int mNextStartY;
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
Context context, List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
int trgY, List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mDestReader = destReader;
mContext = context;
mOccupied = new GridOccupancy(trgX, trgY);
mScreenId = screenId;
@ -362,7 +389,8 @@ public class GridSizeMigrationTaskV2 {
continue;
}
if (findPlacement(entry)) {
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry,
mSrcReader.mTableName, mDestReader.mTableName);
iterator.remove();
}
}
@ -397,14 +425,17 @@ public class GridSizeMigrationTaskV2 {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final DbReader mDestReader;
private final Context mContext;
private final HotseatOccupancy mOccupied;
private final List<DbEntry> mItemsToPlace;
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
Context context, int hotseatSize, List<DbEntry> placedHotseatItems,
List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mDestReader = destReader;
mContext = context;
mOccupied = new HotseatOccupancy(hotseatSize);
for (DbEntry entry : placedHotseatItems) {
@ -422,7 +453,8 @@ public class GridSizeMigrationTaskV2 {
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry,
mSrcReader.mTableName, mDestReader.mTableName);
mOccupied.markCells(entry, true);
}
}
@ -519,7 +551,7 @@ public class GridSizeMigrationTaskV2 {
}
mHotseatEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mHotseatEntries;
}
@ -639,7 +671,7 @@ public class GridSizeMigrationTaskV2 {
}
mWorkspaceEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mWorkspaceEntries;
}
@ -657,7 +689,7 @@ public class GridSizeMigrationTaskV2 {
total++;
entry.mFolderItems.add(intent);
} catch (Exception e) {
removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
removeEntryFromDb(mDb, mTableName, IntArray.wrap(c.getInt(0)));
}
}
c.close();

View File

@ -129,6 +129,7 @@ public class LauncherDbUtils {
toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
toDb.execSQL(
"INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
toDb.execSQL("DETACH DATABASE 'from_db'");
} else {
toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
}