Loading src/com/android/launcher3/model/GridSizeMigrationUtil.java +63 −41 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ import java.util.stream.Collectors; public class GridSizeMigrationUtil { private static final String TAG = "GridSizeMigrationUtil"; private static final boolean DEBUG = false; private static final boolean DEBUG = true; private GridSizeMigrationUtil() { // Util class should not be instantiated Loading Loading @@ -188,27 +188,54 @@ public class GridSizeMigrationUtil { @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 List<DbEntry> srcHotseatItems = srcReader.loadHotseatEntries(); final List<DbEntry> srcWorkspaceItems = srcReader.loadAllWorkspaceEntries(); final List<DbEntry> dstHotseatItems = destReader.loadHotseatEntries(); final List<DbEntry> dstWorkspaceItems = destReader.loadAllWorkspaceEntries(); final List<DbEntry> hotseatToBeAdded = new ArrayList<>(1); final List<DbEntry> workspaceToBeAdded = new ArrayList<>(1); final IntArray toBeRemoved = new IntArray(); calcDiff(srcHotseatItems, dstHotseatItems, hotseatToBeAdded, toBeRemoved); calcDiff(srcWorkspaceItems, dstWorkspaceItems, workspaceToBeAdded, toBeRemoved); final int trgX = targetSize.x; final int trgY = targetSize.y; if (hotseatDiff.isEmpty() && workspaceDiff.isEmpty()) { if (DEBUG) { Log.d(TAG, "Start migration:" + "\n Source Device:" + srcWorkspaceItems.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Target Device:" + dstWorkspaceItems.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Removing Items:" + dstWorkspaceItems.stream().filter(entry -> toBeRemoved.contains(entry.id)).map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Adding Workspace Items:" + workspaceToBeAdded.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Adding Hotseat Items:" + hotseatToBeAdded.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) ); } if (!toBeRemoved.isEmpty()) { removeEntryFromDb(destReader.mDb, destReader.mTableName, toBeRemoved); } if (hotseatToBeAdded.isEmpty() && workspaceToBeAdded.isEmpty()) { return false; } // Sort the items by the reading order. Collections.sort(hotseatDiff); Collections.sort(workspaceDiff); Collections.sort(hotseatToBeAdded); Collections.sort(workspaceToBeAdded); // Migrate hotseat solveHotseatPlacement(db, srcReader, destReader, context, destHotseatSize, hotseatItems, hotseatDiff); destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded); // Migrate workspace. // First we create a collection of the screens Loading @@ -229,8 +256,8 @@ public class GridSizeMigrationUtil { Log.d(TAG, "Migrating " + screenId); } solveGridPlacement(db, srcReader, destReader, context, screenId, trgX, trgY, workspaceDiff, false); if (workspaceDiff.isEmpty()) { destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false); if (workspaceToBeAdded.isEmpty()) { break; } } Loading @@ -238,42 +265,37 @@ public class GridSizeMigrationUtil { // 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 = destReader.mLastScreenId + 1; while (!workspaceDiff.isEmpty()) { while (!workspaceToBeAdded.isEmpty()) { solveGridPlacement(db, srcReader, destReader, context, screenId, trgX, trgY, workspaceDiff, preservePages); destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages); screenId++; } return true; } /** Return what's in the src but not in the dest */ private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) { Map<String, Integer> destIdSet = new HashMap<>(); for (DbEntry entry : dest) { String entryID = entry.getEntryMigrationId(); if (destIdSet.containsKey(entryID)) { destIdSet.put(entryID, destIdSet.get(entryID) + 1); } else { destIdSet.put(entryID, 1); } } List<DbEntry> diff = new ArrayList<>(); for (DbEntry entry : src) { String entryID = entry.getEntryMigrationId(); if (destIdSet.containsKey(entryID)) { Integer count = destIdSet.get(entryID); if (count <= 0) { diff.add(entry); destIdSet.remove(entryID); } else { destIdSet.put(entryID, count - 1); } } else { diff.add(entry); /** * Calculate the differences between {@code src} (denoted by A) and {@code dest} * (denoted by B). * All DbEntry in A - B will be added to {@code toBeAdded} * All DbEntry.id in B - A will be added to {@code toBeRemoved} */ private static void calcDiff(@NonNull final List<DbEntry> src, @NonNull final List<DbEntry> dest, @NonNull final List<DbEntry> toBeAdded, @NonNull final IntArray toBeRemoved) { src.forEach(entry -> { if (!dest.contains(entry)) { toBeAdded.add(entry); } }); dest.forEach(entry -> { if (!src.contains(entry)) { toBeRemoved.add(entry.id); if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { entry.mFolderItems.values().forEach(ids -> ids.forEach(toBeRemoved::add)); } } return diff; }); } private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry, Loading Loading @@ -682,12 +704,12 @@ public class GridSizeMigrationUtil { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DbEntry entry = (DbEntry) o; return Objects.equals(mIntent, entry.mIntent); return Objects.equals(getEntryMigrationId(), entry.getEntryMigrationId()); } @Override public int hashCode() { return Objects.hash(mIntent); return Objects.hash(getEntryMigrationId()); } public void updateContentValues(ContentValues values) { Loading tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +221 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.launcher3.model import android.content.Context import android.content.Intent import android.database.Cursor; import android.database.sqlite.SQLiteDatabase import android.graphics.Point import android.os.Process Loading Loading @@ -183,15 +184,232 @@ class GridSizeMigrationUtilTest { // Expected dest grid icons // _ _ _ _ // 5 6 7 8 // 9 _ 10_ // 9 _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(6) assertThat(locMap.size.toLong()).isEqualTo(5) assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1)) assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1)) assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1)) assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2)) assertThat(locMap[testPackage10]).isEqualTo(Point(2, 2)) } /** * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is * not needed anymore */ @Test @Throws(Exception::class) fun testMigrationBackAndForth() { // Hotseat items in grid A // 1 2 _ 3 4 modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI) modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI) modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI) // Workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 // _ _ 6 _ 7 // _ _ 8 _ _ // _ _ _ _ _ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI) // Hotseat items in grid B // 2 _ _ _ modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2) // Workspace items in grid B // _ _ _ _ // _ _ _ 10 // _ _ _ _ // _ _ _ _ modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10) idp.numDatabaseHotseatIcons = 4 idp.numColumns = 4 idp.numRows = 4 val readerGridA = DbReader(db, TMP_TABLE, context, validPackages) val readerGridB = DbReader(db, TABLE_NAME, context, validPackages) // migrate from A -> B GridSizeMigrationUtil.migrate( context, db, readerGridA, readerGridB, idp.numDatabaseHotseatIcons, Point(idp.numColumns, idp.numRows), DeviceGridState(context), DeviceGridState(idp) ) // Check hotseat items in grid B var c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 verifyHotseat(c, idp, mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) // Check workspace items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() var locMap = parseLocMap(context, c) // Expected items in grid B // _ _ _ _ // 5 6 7 8 // _ _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(4) assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1)) assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 2, 1)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1)) // add item in B modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9) // migrate from B -> A GridSizeMigrationUtil.migrate( context, db, readerGridB, readerGridA, 5, Point(5, 5), DeviceGridState(idp), DeviceGridState(context) ) // Check hotseat items in grid A c = context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid A // 1 2 _ 3 4 verifyHotseat(c, idp, mutableListOf( testPackage1, testPackage2, null, testPackage3, testPackage4).toList()) // Check workspace items in grid A c = context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 // 9 _ 6 _ 7 // _ _ 8 _ _ // _ _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(5) // Verify items that existed in grid A remains in same position assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 4, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 2, 2)) assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 4, 2)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 2, 3)) // Verify items that didn't exist in grid A are added in new screen assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2)) // remove item from B modelHelper.deleteItem(7, TMP_TABLE) // migrate from A -> B GridSizeMigrationUtil.migrate( context, db, readerGridA, readerGridB, idp.numDatabaseHotseatIcons, Point(idp.numColumns, idp.numRows), DeviceGridState(context), DeviceGridState(idp) ) // Check hotseat items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 verifyHotseat(c, idp, mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) // Check workspace items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid B // _ _ _ _ // 5 6 _ 8 // 9 _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(4) assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1)) assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2)) } private fun verifyHotseat(c: Cursor, idp: InvariantDeviceProfile, expected: List<String?>) { assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons) val screenIndex = c.getColumnIndex(SCREEN) val intentIndex = c.getColumnIndex(INTENT) expected.forEachIndexed { idx, pkg -> if (pkg == null) return@forEachIndexed c.moveToNext() assertThat(c.getInt(screenIndex).toLong()).isEqualTo(idx) assertThat(c.getString(intentIndex)).contains(pkg) } c.close() } private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> { // Check workspace items val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) val cellXIndex = c.getColumnIndex(CELLX) val cellYIndex = c.getColumnIndex(CELLY) val locMap = mutableMapOf<String, Triple<Int, Int, Int>>() while (c.moveToNext()) { locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] = Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex)) } c.close() return locMap.toMap() } @Test Loading tests/src/com/android/launcher3/util/LauncherModelHelper.java +6 −0 Original line number Diff line number Diff line Loading @@ -362,6 +362,12 @@ public class LauncherModelHelper { sandboxContext.getContentResolver().insert(contentUri, values); } public void deleteItem(int itemId, @NonNull final String tableName) { final Uri uri = Uri.parse("content://" + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId); sandboxContext.getContentResolver().delete(uri, null, null); } public int[][][] createGrid(int[][][] typeArray) { return createGrid(typeArray, 1); } Loading Loading
src/com/android/launcher3/model/GridSizeMigrationUtil.java +63 −41 Original line number Diff line number Diff line Loading @@ -66,7 +66,7 @@ import java.util.stream.Collectors; public class GridSizeMigrationUtil { private static final String TAG = "GridSizeMigrationUtil"; private static final boolean DEBUG = false; private static final boolean DEBUG = true; private GridSizeMigrationUtil() { // Util class should not be instantiated Loading Loading @@ -188,27 +188,54 @@ public class GridSizeMigrationUtil { @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 List<DbEntry> srcHotseatItems = srcReader.loadHotseatEntries(); final List<DbEntry> srcWorkspaceItems = srcReader.loadAllWorkspaceEntries(); final List<DbEntry> dstHotseatItems = destReader.loadHotseatEntries(); final List<DbEntry> dstWorkspaceItems = destReader.loadAllWorkspaceEntries(); final List<DbEntry> hotseatToBeAdded = new ArrayList<>(1); final List<DbEntry> workspaceToBeAdded = new ArrayList<>(1); final IntArray toBeRemoved = new IntArray(); calcDiff(srcHotseatItems, dstHotseatItems, hotseatToBeAdded, toBeRemoved); calcDiff(srcWorkspaceItems, dstWorkspaceItems, workspaceToBeAdded, toBeRemoved); final int trgX = targetSize.x; final int trgY = targetSize.y; if (hotseatDiff.isEmpty() && workspaceDiff.isEmpty()) { if (DEBUG) { Log.d(TAG, "Start migration:" + "\n Source Device:" + srcWorkspaceItems.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Target Device:" + dstWorkspaceItems.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Removing Items:" + dstWorkspaceItems.stream().filter(entry -> toBeRemoved.contains(entry.id)).map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Adding Workspace Items:" + workspaceToBeAdded.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) + "\n Adding Hotseat Items:" + hotseatToBeAdded.stream().map(DbEntry::toString).collect( Collectors.joining(",\n", "[", "]")) ); } if (!toBeRemoved.isEmpty()) { removeEntryFromDb(destReader.mDb, destReader.mTableName, toBeRemoved); } if (hotseatToBeAdded.isEmpty() && workspaceToBeAdded.isEmpty()) { return false; } // Sort the items by the reading order. Collections.sort(hotseatDiff); Collections.sort(workspaceDiff); Collections.sort(hotseatToBeAdded); Collections.sort(workspaceToBeAdded); // Migrate hotseat solveHotseatPlacement(db, srcReader, destReader, context, destHotseatSize, hotseatItems, hotseatDiff); destReader, context, destHotseatSize, dstHotseatItems, hotseatToBeAdded); // Migrate workspace. // First we create a collection of the screens Loading @@ -229,8 +256,8 @@ public class GridSizeMigrationUtil { Log.d(TAG, "Migrating " + screenId); } solveGridPlacement(db, srcReader, destReader, context, screenId, trgX, trgY, workspaceDiff, false); if (workspaceDiff.isEmpty()) { destReader, context, screenId, trgX, trgY, workspaceToBeAdded, false); if (workspaceToBeAdded.isEmpty()) { break; } } Loading @@ -238,42 +265,37 @@ public class GridSizeMigrationUtil { // 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 = destReader.mLastScreenId + 1; while (!workspaceDiff.isEmpty()) { while (!workspaceToBeAdded.isEmpty()) { solveGridPlacement(db, srcReader, destReader, context, screenId, trgX, trgY, workspaceDiff, preservePages); destReader, context, screenId, trgX, trgY, workspaceToBeAdded, preservePages); screenId++; } return true; } /** Return what's in the src but not in the dest */ private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) { Map<String, Integer> destIdSet = new HashMap<>(); for (DbEntry entry : dest) { String entryID = entry.getEntryMigrationId(); if (destIdSet.containsKey(entryID)) { destIdSet.put(entryID, destIdSet.get(entryID) + 1); } else { destIdSet.put(entryID, 1); } } List<DbEntry> diff = new ArrayList<>(); for (DbEntry entry : src) { String entryID = entry.getEntryMigrationId(); if (destIdSet.containsKey(entryID)) { Integer count = destIdSet.get(entryID); if (count <= 0) { diff.add(entry); destIdSet.remove(entryID); } else { destIdSet.put(entryID, count - 1); } } else { diff.add(entry); /** * Calculate the differences between {@code src} (denoted by A) and {@code dest} * (denoted by B). * All DbEntry in A - B will be added to {@code toBeAdded} * All DbEntry.id in B - A will be added to {@code toBeRemoved} */ private static void calcDiff(@NonNull final List<DbEntry> src, @NonNull final List<DbEntry> dest, @NonNull final List<DbEntry> toBeAdded, @NonNull final IntArray toBeRemoved) { src.forEach(entry -> { if (!dest.contains(entry)) { toBeAdded.add(entry); } }); dest.forEach(entry -> { if (!src.contains(entry)) { toBeRemoved.add(entry.id); if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) { entry.mFolderItems.values().forEach(ids -> ids.forEach(toBeRemoved::add)); } } return diff; }); } private static void insertEntryInDb(SQLiteDatabase db, Context context, DbEntry entry, Loading Loading @@ -682,12 +704,12 @@ public class GridSizeMigrationUtil { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DbEntry entry = (DbEntry) o; return Objects.equals(mIntent, entry.mIntent); return Objects.equals(getEntryMigrationId(), entry.getEntryMigrationId()); } @Override public int hashCode() { return Objects.hash(mIntent); return Objects.hash(getEntryMigrationId()); } public void updateContentValues(ContentValues values) { Loading
tests/src/com/android/launcher3/model/GridSizeMigrationUtilTest.kt +221 −3 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.launcher3.model import android.content.Context import android.content.Intent import android.database.Cursor; import android.database.sqlite.SQLiteDatabase import android.graphics.Point import android.os.Process Loading Loading @@ -183,15 +184,232 @@ class GridSizeMigrationUtilTest { // Expected dest grid icons // _ _ _ _ // 5 6 7 8 // 9 _ 10_ // 9 _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(6) assertThat(locMap.size.toLong()).isEqualTo(5) assertThat(locMap[testPackage5]).isEqualTo(Point(0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Point(1, 1)) assertThat(locMap[testPackage7]).isEqualTo(Point(2, 1)) assertThat(locMap[testPackage8]).isEqualTo(Point(3, 1)) assertThat(locMap[testPackage9]).isEqualTo(Point(0, 2)) assertThat(locMap[testPackage10]).isEqualTo(Point(2, 2)) } /** * Old migration logic, should be modified once [FeatureFlags.ENABLE_NEW_MIGRATION_LOGIC] is * not needed anymore */ @Test @Throws(Exception::class) fun testMigrationBackAndForth() { // Hotseat items in grid A // 1 2 _ 3 4 modelHelper.addItem(APP_ICON, 0, HOTSEAT, 0, 0, testPackage1, 1, TMP_CONTENT_URI) modelHelper.addItem(SHORTCUT, 1, HOTSEAT, 0, 0, testPackage2, 2, TMP_CONTENT_URI) modelHelper.addItem(SHORTCUT, 3, HOTSEAT, 0, 0, testPackage3, 3, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 4, HOTSEAT, 0, 0, testPackage4, 4, TMP_CONTENT_URI) // Workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 // _ _ 6 _ 7 // _ _ 8 _ _ // _ _ _ _ _ modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 1, testPackage5, 5, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 2, testPackage6, 6, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 4, 2, testPackage7, 7, TMP_CONTENT_URI) modelHelper.addItem(APP_ICON, 0, DESKTOP, 2, 3, testPackage8, 8, TMP_CONTENT_URI) // Hotseat items in grid B // 2 _ _ _ modelHelper.addItem(SHORTCUT, 0, HOTSEAT, 0, 0, testPackage2) // Workspace items in grid B // _ _ _ _ // _ _ _ 10 // _ _ _ _ // _ _ _ _ modelHelper.addItem(APP_ICON, 0, DESKTOP, 1, 3, testPackage10) idp.numDatabaseHotseatIcons = 4 idp.numColumns = 4 idp.numRows = 4 val readerGridA = DbReader(db, TMP_TABLE, context, validPackages) val readerGridB = DbReader(db, TABLE_NAME, context, validPackages) // migrate from A -> B GridSizeMigrationUtil.migrate( context, db, readerGridA, readerGridB, idp.numDatabaseHotseatIcons, Point(idp.numColumns, idp.numRows), DeviceGridState(context), DeviceGridState(idp) ) // Check hotseat items in grid B var c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 verifyHotseat(c, idp, mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) // Check workspace items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() var locMap = parseLocMap(context, c) // Expected items in grid B // _ _ _ _ // 5 6 7 8 // _ _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(4) assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1)) assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 2, 1)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1)) // add item in B modelHelper.addItem(APP_ICON, 0, DESKTOP, 0, 2, testPackage9) // migrate from B -> A GridSizeMigrationUtil.migrate( context, db, readerGridB, readerGridA, 5, Point(5, 5), DeviceGridState(idp), DeviceGridState(context) ) // Check hotseat items in grid A c = context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid A // 1 2 _ 3 4 verifyHotseat(c, idp, mutableListOf( testPackage1, testPackage2, null, testPackage3, testPackage4).toList()) // Check workspace items in grid A c = context.contentResolver.query( TMP_CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid A // _ _ _ _ _ // _ _ _ _ 5 // 9 _ 6 _ 7 // _ _ 8 _ _ // _ _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(5) // Verify items that existed in grid A remains in same position assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 4, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 2, 2)) assertThat(locMap[testPackage7]).isEqualTo(Triple(0, 4, 2)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 2, 3)) // Verify items that didn't exist in grid A are added in new screen assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2)) // remove item from B modelHelper.deleteItem(7, TMP_TABLE) // migrate from A -> B GridSizeMigrationUtil.migrate( context, db, readerGridA, readerGridB, idp.numDatabaseHotseatIcons, Point(idp.numColumns, idp.numRows), DeviceGridState(context), DeviceGridState(idp) ) // Check hotseat items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, INTENT), "container=$CONTAINER_HOTSEAT", null, SCREEN, null ) ?: throw IllegalStateException() // Expected hotseat items in grid B // 2 1 3 4 verifyHotseat(c, idp, mutableListOf(testPackage2, testPackage1, testPackage3, testPackage4).toList()) // Check workspace items in grid B c = context.contentResolver.query( CONTENT_URI, arrayOf(SCREEN, CELLX, CELLY, INTENT), "container=$CONTAINER_DESKTOP", null, null, null ) ?: throw IllegalStateException() locMap = parseLocMap(context, c) // Expected workspace items in grid B // _ _ _ _ // 5 6 _ 8 // 9 _ _ _ // _ _ _ _ assertThat(locMap.size.toLong()).isEqualTo(4) assertThat(locMap[testPackage5]).isEqualTo(Triple(0, 0, 1)) assertThat(locMap[testPackage6]).isEqualTo(Triple(0, 1, 1)) assertThat(locMap[testPackage8]).isEqualTo(Triple(0, 3, 1)) assertThat(locMap[testPackage9]).isEqualTo(Triple(0, 0, 2)) } private fun verifyHotseat(c: Cursor, idp: InvariantDeviceProfile, expected: List<String?>) { assertThat(c.count).isEqualTo(idp.numDatabaseHotseatIcons) val screenIndex = c.getColumnIndex(SCREEN) val intentIndex = c.getColumnIndex(INTENT) expected.forEachIndexed { idx, pkg -> if (pkg == null) return@forEachIndexed c.moveToNext() assertThat(c.getInt(screenIndex).toLong()).isEqualTo(idx) assertThat(c.getString(intentIndex)).contains(pkg) } c.close() } private fun parseLocMap(context: Context, c: Cursor): Map<String, Triple<Int, Int, Int>> { // Check workspace items val intentIndex = c.getColumnIndex(INTENT) val screenIndex = c.getColumnIndex(SCREEN) val cellXIndex = c.getColumnIndex(CELLX) val cellYIndex = c.getColumnIndex(CELLY) val locMap = mutableMapOf<String, Triple<Int, Int, Int>>() while (c.moveToNext()) { locMap[Intent.parseUri(c.getString(intentIndex), 0).getPackage()] = Triple(c.getInt(screenIndex), c.getInt(cellXIndex), c.getInt(cellYIndex)) } c.close() return locMap.toMap() } @Test Loading
tests/src/com/android/launcher3/util/LauncherModelHelper.java +6 −0 Original line number Diff line number Diff line Loading @@ -362,6 +362,12 @@ public class LauncherModelHelper { sandboxContext.getContentResolver().insert(contentUri, values); } public void deleteItem(int itemId, @NonNull final String tableName) { final Uri uri = Uri.parse("content://" + LauncherProvider.AUTHORITY + "/" + tableName + "/" + itemId); sandboxContext.getContentResolver().delete(uri, null, null); } public int[][][] createGrid(int[][][] typeArray) { return createGrid(typeArray, 1); } Loading