Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ad932045 authored by Pinyao Ting's avatar Pinyao Ting Committed by Android (Google) Code Review
Browse files

Merge "Refactor GridSizeMigrationTaskV2" into tm-qpr-dev

parents e57cab1d dec4305d
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -50,7 +50,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.GridSizeMigrationTaskV2;
import com.android.launcher3.model.GridSizeMigrationUtil;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
@@ -241,10 +241,10 @@ public class PreviewSurfaceRenderer {

    @WorkerThread
    private boolean doGridMigrationIfNecessary() {
        if (!GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)) {
        if (!GridSizeMigrationUtil.needsToMigrate(mContext, mIdp)) {
            return false;
        }
        return GridSizeMigrationTaskV2.migrateGridIfNeeded(mContext, mIdp);
        return GridSizeMigrationUtil.migrateGridIfNeeded(mContext, mIdp);
    }

    @UiThread
+116 −196
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@ import android.util.ArrayMap;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherAppState;
@@ -64,42 +63,13 @@ import java.util.stream.Collectors;
 * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
 * result of restoring from a larger device or device density change.
 */
public class GridSizeMigrationTaskV2 {
public class GridSizeMigrationUtil {

    private static final String TAG = "GridSizeMigrationTaskV2";
    private static final String TAG = "GridSizeMigrationUtil";
    private static final boolean DEBUG = false;

    private final Context mContext;
    private final SQLiteDatabase mDb;
    private final DbReader mSrcReader;
    private final DbReader mDestReader;

    private final List<DbEntry> mHotseatItems;
    private final List<DbEntry> mWorkspaceItems;

    private final List<DbEntry> mHotseatDiff;
    private final List<DbEntry> mWorkspaceDiff;

    private final int mDestHotseatSize;
    private final int mTrgX, mTrgY;

    @VisibleForTesting
    protected GridSizeMigrationTaskV2(Context context, SQLiteDatabase db, DbReader srcReader,
            DbReader destReader, int destHotseatSize, Point targetSize) {
        mContext = context;
        mDb = db;
        mSrcReader = srcReader;
        mDestReader = destReader;

        mHotseatItems = destReader.loadHotseatEntries();
        mWorkspaceItems = destReader.loadAllWorkspaceEntries();

        mHotseatDiff = calcDiff(mSrcReader.loadHotseatEntries(), mHotseatItems);
        mWorkspaceDiff = calcDiff(mSrcReader.loadAllWorkspaceEntries(), mWorkspaceItems);
        mDestHotseatSize = destHotseatSize;

        mTrgX = targetSize.x;
        mTrgY = targetSize.y;
    private GridSizeMigrationUtil() {
        // Util class should not be instantiated
    }

    /**
@@ -187,9 +157,8 @@ public class GridSizeMigrationTaskV2 {
                    context, validPackages);

            Point targetSize = new Point(destDeviceState.getColumns(), destDeviceState.getRows());
            GridSizeMigrationTaskV2 task = new GridSizeMigrationTaskV2(context, t.getDb(),
                    srcReader, destReader, destDeviceState.getNumHotseat(), targetSize);
            task.migrate(srcDeviceState, destDeviceState);
            migrate(context, t.getDb(), srcReader, destReader, destDeviceState.getNumHotseat(),
                    targetSize, srcDeviceState, destDeviceState);

            if (!migrateForPreview) {
                dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
@@ -212,25 +181,39 @@ public class GridSizeMigrationTaskV2 {
        }
    }

    @VisibleForTesting
    protected boolean migrate(DeviceGridState srcDeviceState, DeviceGridState destDeviceState) {
        if (mHotseatDiff.isEmpty() && mWorkspaceDiff.isEmpty()) {
    public static boolean migrate(
            @NonNull final Context context, @NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            final int destHotseatSize, @NonNull final Point targetSize,
            @NonNull final DeviceGridState srcDeviceState,
            @NonNull final DeviceGridState destDeviceState) {

        final List<DbEntry> hotseatItems = destReader.loadHotseatEntries();
        final List<DbEntry> workspaceItems = destReader.loadAllWorkspaceEntries();
        final List<DbEntry> hotseatDiff =
                calcDiff(srcReader.loadHotseatEntries(), hotseatItems);
        final List<DbEntry> workspaceDiff =
                calcDiff(srcReader.loadAllWorkspaceEntries(), workspaceItems);

        final int trgX = targetSize.x;
        final int trgY = targetSize.y;

        if (hotseatDiff.isEmpty() && workspaceDiff.isEmpty()) {
            return false;
        }

        // Sort the items by the reading order.
        Collections.sort(mHotseatDiff);
        Collections.sort(mWorkspaceDiff);
        Collections.sort(hotseatDiff);
        Collections.sort(workspaceDiff);

        // Migrate hotseat
        HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
                mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
        hotseatSolution.find();
        solveHotseatPlacement(db, srcReader,
                destReader, context, destHotseatSize, hotseatItems, hotseatDiff);

        // Migrate workspace.
        // First we create a collection of the screens
        List<Integer> screens = new ArrayList<>();
        for (int screenId = 0; screenId <= mDestReader.mLastScreenId; screenId++) {
        for (int screenId = 0; screenId <= destReader.mLastScreenId; screenId++) {
            screens.add(screenId);
        }

@@ -245,22 +228,19 @@ public class GridSizeMigrationTaskV2 {
            if (DEBUG) {
                Log.d(TAG, "Migrating " + screenId);
            }
            GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff, false);
            workspaceSolution.find();
            if (mWorkspaceDiff.isEmpty()) {
            solveGridPlacement(db, srcReader,
                    destReader, context, screenId, trgX, trgY, workspaceDiff, false);
            if (workspaceDiff.isEmpty()) {
                break;
            }
        }

        // In case the new grid is smaller, there might be some leftover items that don't fit on
        // any of the screens, in this case we add them to new screens until all of them are placed.
        int screenId = mDestReader.mLastScreenId + 1;
        while (!mWorkspaceDiff.isEmpty()) {
            GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
                    mDestReader, mContext, screenId, mTrgX, mTrgY, mWorkspaceDiff,
                    preservePages);
            workspaceSolution.find();
        int screenId = destReader.mLastScreenId + 1;
        while (!workspaceDiff.isEmpty()) {
            solveGridPlacement(db, srcReader,
                    destReader, context, screenId, trgX, trgY, workspaceDiff, preservePages);
            screenId++;
        }

@@ -365,59 +345,31 @@ public class GridSizeMigrationTaskV2 {
        return validPackages;
    }

    protected static class GridPlacementSolution {

        private final SQLiteDatabase mDb;
        private final DbReader mSrcReader;
        private final DbReader mDestReader;
        private final Context mContext;
        private final GridOccupancy mOccupied;
        private final int mScreenId;
        private final int mTrgX;
        private final int mTrgY;
        private final List<DbEntry> mSortedItemsToPlace;
        private final boolean mMatchingScreenIdOnly;

        private int mNextStartX;
        private int mNextStartY;

        GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
                Context context, int screenId, int trgX, int trgY, List<DbEntry> sortedItemsToPlace,
                boolean matchingScreenIdOnly) {
            mDb = db;
            mSrcReader = srcReader;
            mDestReader = destReader;
            mContext = context;
            mOccupied = new GridOccupancy(trgX, trgY);
            mScreenId = screenId;
            mTrgX = trgX;
            mTrgY = trgY;
            mNextStartX = 0;
            mNextStartY = mScreenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
                    ? 1 /* smartspace */ : 0;
            List<DbEntry> existedEntries = mDestReader.mWorkspaceEntriesByScreenId.get(screenId);
    private static void solveGridPlacement(@NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            @NonNull final Context context, final int screenId, final int trgX, final int trgY,
            @NonNull final List<DbEntry> sortedItemsToPlace, final boolean matchingScreenIdOnly) {
        final GridOccupancy occupied = new GridOccupancy(trgX, trgY);
        final Point trg = new Point(trgX, trgY);
        final Point next = new Point(0, screenId == 0 && FeatureFlags.QSB_ON_FIRST_SCREEN
                ? 1 /* smartspace */ : 0);
        List<DbEntry> existedEntries = destReader.mWorkspaceEntriesByScreenId.get(screenId);
        if (existedEntries != null) {
            for (DbEntry entry : existedEntries) {
                    mOccupied.markCells(entry, true);
                occupied.markCells(entry, true);
            }
        }
            mSortedItemsToPlace = sortedItemsToPlace;
            mMatchingScreenIdOnly = matchingScreenIdOnly;
        }

        public void find() {
            Iterator<DbEntry> iterator = mSortedItemsToPlace.iterator();
        Iterator<DbEntry> iterator = sortedItemsToPlace.iterator();
        while (iterator.hasNext()) {
            final DbEntry entry = iterator.next();
                if (mMatchingScreenIdOnly && entry.screenId < mScreenId) continue;
                if (mMatchingScreenIdOnly && entry.screenId > mScreenId) break;
                if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
            if (matchingScreenIdOnly && entry.screenId < screenId) continue;
            if (matchingScreenIdOnly && entry.screenId > screenId) break;
            if (entry.minSpanX > trgX || entry.minSpanY > trgY) {
                iterator.remove();
                continue;
            }
                if (findPlacement(entry)) {
                    insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
                            mDestReader.mTableName);
            if (findPlacementForEntry(entry, next, trg, occupied, screenId)) {
                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                iterator.remove();
            }
        }
@@ -428,81 +380,53 @@ public class GridSizeMigrationTaskV2 {
     * a memoization of last placement, we can start our search for next placement from there
     * to speed up the search.
     */
        private boolean findPlacement(DbEntry entry) {
            for (int y = mNextStartY; y <  mTrgY; y++) {
                for (int x = mNextStartX; x < mTrgX; x++) {
                    boolean fits = mOccupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
                    boolean minFits = mOccupied.isRegionVacant(x, y, entry.minSpanX,
    private static boolean findPlacementForEntry(@NonNull final DbEntry entry,
            @NonNull final Point next, @NonNull final Point trg,
            @NonNull final GridOccupancy occupied, final int screenId) {
        for (int y = next.y; y <  trg.y; y++) {
            for (int x = next.x; x < trg.x; x++) {
                boolean fits = occupied.isRegionVacant(x, y, entry.spanX, entry.spanY);
                boolean minFits = occupied.isRegionVacant(x, y, entry.minSpanX,
                        entry.minSpanY);
                if (minFits) {
                    entry.spanX = entry.minSpanX;
                    entry.spanY = entry.minSpanY;
                }
                if (fits || minFits) {
                        entry.screenId = mScreenId;
                    entry.screenId = screenId;
                    entry.cellX = x;
                    entry.cellY = y;
                        mOccupied.markCells(entry, true);
                        mNextStartX = x + entry.spanX;
                        mNextStartY = y;
                    occupied.markCells(entry, true);
                    next.set(x + entry.spanX, y);
                    return true;
                }
            }
                mNextStartX = 0;
            next.set(0, next.y);
        }
        return false;
    }
    }

    protected static class HotseatPlacementSolution {

        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;
    private static void solveHotseatPlacement(@NonNull final SQLiteDatabase db,
            @NonNull final DbReader srcReader, @NonNull final DbReader destReader,
            @NonNull final Context context, final int hotseatSize,
            @NonNull final  List<DbEntry> placedHotseatItems,
            @NonNull final 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);
        final boolean[] occupied = new boolean[hotseatSize];
        for (DbEntry entry : placedHotseatItems) {
                mOccupied.markCells(entry, true);
            }
            mItemsToPlace = itemsToPlace;
            occupied[entry.screenId] = true;
        }

        public void find() {
            for (int i = 0; i < mOccupied.mCells.length; i++) {
                if (!mOccupied.mCells[i] && !mItemsToPlace.isEmpty()) {
                    DbEntry entry = mItemsToPlace.remove(0);
        for (int i = 0; i < occupied.length; i++) {
            if (!occupied[i] && !itemsToPlace.isEmpty()) {
                DbEntry entry = itemsToPlace.remove(0);
                entry.screenId = i;
                // These values does not affect the item position, but we should set them
                // to something other than -1.
                entry.cellX = i;
                entry.cellY = 0;
                    insertEntryInDb(mDb, mContext, entry, mSrcReader.mTableName,
                            mDestReader.mTableName);
                    mOccupied.markCells(entry, true);
                }
            }
        }

        private class HotseatOccupancy {

            private final boolean[] mCells;

            private HotseatOccupancy(int hotseatSize) {
                mCells = new boolean[hotseatSize];
            }

            private void markCells(ItemInfo item, boolean value) {
                mCells[item.screenId] = value;
                insertEntryInDb(db, context, entry, srcReader.mTableName, destReader.mTableName);
                occupied[entry.screenId] = true;
            }
        }
    }
@@ -515,8 +439,6 @@ public class GridSizeMigrationTaskV2 {
        private final Set<String> mValidPackages;
        private int mLastScreenId = -1;

        private final ArrayList<DbEntry> mHotseatEntries = new ArrayList<>();
        private final ArrayList<DbEntry> mWorkspaceEntries = new ArrayList<>();
        private final Map<Integer, ArrayList<DbEntry>> mWorkspaceEntriesByScreenId =
                new ArrayMap<>();

@@ -528,7 +450,8 @@ public class GridSizeMigrationTaskV2 {
            mValidPackages = validPackages;
        }

        protected ArrayList<DbEntry> loadHotseatEntries() {
        protected List<DbEntry> loadHotseatEntries() {
            final List<DbEntry> hotseatEntries = new ArrayList<>();
            Cursor c = queryWorkspace(
                    new String[]{
                            LauncherSettings.Favorites._ID,                  // 0
@@ -577,14 +500,15 @@ public class GridSizeMigrationTaskV2 {
                    entriesToRemove.add(entry.id);
                    continue;
                }
                mHotseatEntries.add(entry);
                hotseatEntries.add(entry);
            }
            removeEntryFromDb(mDb, mTableName, entriesToRemove);
            c.close();
            return mHotseatEntries;
            return hotseatEntries;
        }

        protected ArrayList<DbEntry> loadAllWorkspaceEntries() {
        protected List<DbEntry> loadAllWorkspaceEntries() {
            final List<DbEntry> workspaceEntries = new ArrayList<>();
            Cursor c = queryWorkspace(
                    new String[]{
                            LauncherSettings.Favorites._ID,                  // 0
@@ -599,10 +523,6 @@ public class GridSizeMigrationTaskV2 {
                            LauncherSettings.Favorites.APPWIDGET_ID},        // 9
                        LauncherSettings.Favorites.CONTAINER + " = "
                            + LauncherSettings.Favorites.CONTAINER_DESKTOP);
            return loadWorkspaceEntries(c);
        }

        private ArrayList<DbEntry> loadWorkspaceEntries(Cursor c) {
            final int indexId = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
            final int indexItemType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
            final int indexScreen = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SCREEN);
@@ -678,7 +598,7 @@ public class GridSizeMigrationTaskV2 {
                    entriesToRemove.add(entry.id);
                    continue;
                }
                mWorkspaceEntries.add(entry);
                workspaceEntries.add(entry);
                if (!mWorkspaceEntriesByScreenId.containsKey(entry.screenId)) {
                    mWorkspaceEntriesByScreenId.put(entry.screenId, new ArrayList<>());
                }
@@ -686,7 +606,7 @@ public class GridSizeMigrationTaskV2 {
            }
            removeEntryFromDb(mDb, mTableName, entriesToRemove);
            c.close();
            return mWorkspaceEntries;
            return workspaceEntries;
        }

        private int getFolderItemsCount(DbEntry entry) {
+1 −1
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ public class LoaderTask implements Runnable {
        final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);

        boolean clearDb = false;
        if (!GridSizeMigrationTaskV2.migrateGridIfNeeded(context)) {
        if (!GridSizeMigrationUtil.migrateGridIfNeeded(context)) {
            // Migration failed. Clear workspace.
            clearDb = true;
        }
+57 −51
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@ import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherFiles
import com.android.launcher3.LauncherSettings.Favorites.*
import com.android.launcher3.config.FeatureFlags
import com.android.launcher3.model.GridSizeMigrationTaskV2.DbReader
import com.android.launcher3.model.GridSizeMigrationUtil.DbReader
import com.android.launcher3.pm.UserCache
import com.android.launcher3.provider.LauncherDbUtils
import com.android.launcher3.util.LauncherModelHelper
@@ -37,10 +37,10 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

/** Unit tests for [GridSizeMigrationTaskV2]  */
/** Unit tests for [GridSizeMigrationUtil]  */
@SmallTest
@RunWith(AndroidJUnit4::class)
class GridSizeMigrationTaskV2Test {
class GridSizeMigrationUtilTest {
    private lateinit var modelHelper: LauncherModelHelper
    private lateinit var context: Context
    private lateinit var db: SQLiteDatabase
@@ -122,15 +122,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        var c = context.contentResolver.query(
@@ -207,15 +208,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        val c = context.contentResolver.query(
@@ -262,15 +264,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Check hotseat items
        val c = context.contentResolver.query(
@@ -327,15 +330,16 @@ class GridSizeMigrationTaskV2Test {

        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(
@@ -387,15 +391,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 5
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(
@@ -448,15 +453,16 @@ class GridSizeMigrationTaskV2Test {
        idp.numRows = 4
        val srcReader = DbReader(db, TMP_TABLE, context, validPackages)
        val destReader = DbReader(db, TABLE_NAME, context, validPackages)
        val task = GridSizeMigrationTaskV2(
        GridSizeMigrationUtil.migrate(
                context,
                db,
                srcReader,
                destReader,
                idp.numDatabaseHotseatIcons,
            Point(idp.numColumns, idp.numRows)
                Point(idp.numColumns, idp.numRows),
                DeviceGridState(context),
                DeviceGridState(idp)
        )
        task.migrate(DeviceGridState(context), DeviceGridState(idp))

        // Get workspace items
        val c = context.contentResolver.query(