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
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a699c32..8d20bd6 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -85,6 +85,7 @@
 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 @@
      */
     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 @@
         }
     }
 
-    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 @@
                 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 @@
         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;
         }
 
         /**
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index f516446..5262b18 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -326,10 +326,16 @@
 
         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);
         }
     }
 }
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index 5bc6610..7d4eb0e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -21,7 +21,6 @@
 
 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 @@
 
         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()
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
index 0bdccfa..79ae4c5 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationTaskV2.java
@@ -123,8 +123,16 @@
     }
 
     /**
-     * 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 @@
         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 @@
                 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 @@
                     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 @@
             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 @@
 
         // 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 @@
             }
             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 @@
         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 @@
     }
 
     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 @@
                 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 @@
                     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 @@
 
         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 @@
         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 @@
                     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 @@
 
         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 @@
                     // 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 @@
                 }
                 mHotseatEntries.add(entry);
             }
-            removeEntryFromDb(mDb, entriesToRemove);
+            removeEntryFromDb(mDb, mTableName, entriesToRemove);
             c.close();
             return mHotseatEntries;
         }
@@ -639,7 +671,7 @@
                 }
                 mWorkspaceEntries.add(entry);
             }
-            removeEntryFromDb(mDb, entriesToRemove);
+            removeEntryFromDb(mDb, mTableName, entriesToRemove);
             c.close();
             return mWorkspaceEntries;
         }
@@ -657,7 +689,7 @@
                     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();
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.java b/src/com/android/launcher3/provider/LauncherDbUtils.java
index dacea84..7e05a5a 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.java
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.java
@@ -129,6 +129,7 @@
             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);
         }