Merge "Fix widget dissapearing because of change of appWidgetId" into main
diff --git a/src/com/android/launcher3/model/GridSizeMigrationUtil.java b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
index 299c952..f24a7c1 100644
--- a/src/com/android/launcher3/model/GridSizeMigrationUtil.java
+++ b/src/com/android/launcher3/model/GridSizeMigrationUtil.java
@@ -49,6 +49,7 @@
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
+import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
@@ -674,6 +675,11 @@
private String mProvider;
private Map<String, Set<Integer>> mFolderItems = new HashMap<>();
+ /**
+ * Id of the specific widget.
+ */
+ public int appWidgetId = NO_ID;
+
/** Comparator according to the reading order */
@Override
public int compareTo(DbEntry another) {
@@ -707,6 +713,18 @@
values.put(LauncherSettings.Favorites.SPANY, spanY);
}
+ @Override
+ public void writeToValues(@NonNull ContentWriter writer) {
+ super.writeToValues(writer);
+ writer.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+ }
+
+ @Override
+ public void readFromValues(@NonNull ContentValues values) {
+ super.readFromValues(values);
+ appWidgetId = values.getAsInteger(LauncherSettings.Favorites.APPWIDGET_ID);
+ }
+
/** This id is not used in the DB is only used while doing the migration and it identifies
* an entry on each workspace. For example two calculator icons would have the same
* migration id even thought they have different database ids.
@@ -717,7 +735,10 @@
case LauncherSettings.Favorites.ITEM_TYPE_APP_PAIR:
return getFolderMigrationId();
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- return mProvider;
+ // mProvider is the app the widget belongs to and appWidgetId it's the unique
+ // is of the widget, we need both because if you remove a widget and then add it
+ // again, then it can change and the WidgetProvider would not know the widget.
+ return mProvider + appWidgetId;
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
final String intentStr = cleanIntentString(mIntent);
try {
diff --git a/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
index e773a86..a006fd7 100644
--- a/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
+++ b/tests/src/com/android/launcher3/celllayout/testgenerator/ValidGridMigrationTestCaseGenerator.kt
@@ -31,16 +31,17 @@
* account for cases where the user have the same item multiple times.
*/
fun generateItemsForTest(
- testCase: GridMigrationUnitTestCase,
- repeatAfter: Int
+ boards: List<CellLayoutBoard>,
+ repeatAfterRange: Point
): List<WorkspaceItem> {
val id = AtomicInteger(0)
val widgetId = AtomicInteger(LauncherAppWidgetInfo.CUSTOM_WIDGET_ID - 1)
- val boards = testCase.boards
// Repeat the same appWidgetProvider and intent to have repeating widgets and icons and test
// that case too
- val getIntent = { i: Int -> "Intent ${i % repeatAfter}" }
- val getProvider = { i: Int -> "com.test/test.Provider${i % repeatAfter}" }
+ val getIntent = { i: Int -> "Intent ${(i + repeatAfterRange.x) % repeatAfterRange.y}" }
+ val getProvider = { i: Int ->
+ "com.test/test.Provider${(i + repeatAfterRange.x) % repeatAfterRange.y }"
+ }
val hotseatEntries =
(0 until boards[0].width).map {
WorkspaceItem(
@@ -97,11 +98,12 @@
container = LauncherSettings.Favorites.CONTAINER_DESKTOP
)
}
- return widgetEntries + hotseatEntries // + iconEntries
+ return widgetEntries + hotseatEntries + iconEntries
}
data class GridMigrationUnitTestCase(
val boards: List<CellLayoutBoard>,
+ val destBoards: List<CellLayoutBoard>,
val srcSize: Point,
val targetSize: Point,
val seed: Long
@@ -135,11 +137,27 @@
return boards
}
- fun generateTestCase(): GridMigrationUnitTestCase {
- var seed = generator.nextLong()
+ fun generateTestCase(isDestEmpty: Boolean): GridMigrationUnitTestCase {
+ val seed = generator.nextLong()
val randomBoardGenerator = RandomBoardGenerator(Random(seed))
val width = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
val height = randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+ val targetSize =
+ Point(
+ randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE),
+ randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
+ )
+ val destBoards =
+ if (isDestEmpty) {
+ listOf()
+ } else {
+ generateBoards(
+ boardGenerator = randomBoardGenerator,
+ width = targetSize.x,
+ height = targetSize.y,
+ boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
+ )
+ }
return GridMigrationUnitTestCase(
boards =
generateBoards(
@@ -148,12 +166,9 @@
height = height,
boardCount = randomBoardGenerator.getRandom(3, MAX_BOARD_COUNT)
),
+ destBoards = destBoards,
srcSize = Point(width, height),
- targetSize =
- Point(
- randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE),
- randomBoardGenerator.getRandom(3, MAX_BOARD_SIZE)
- ),
+ targetSize = targetSize,
seed = seed
)
}
diff --git a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
index 1002976..58b915f 100644
--- a/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
+++ b/tests/src/com/android/launcher3/model/gridmigration/ValidGridMigrationUnitTest.kt
@@ -40,14 +40,22 @@
import org.junit.Test
import org.junit.runner.RunWith
+private data class Grid(val tableName: String, val size: Point, val items: List<WorkspaceItem>) {
+ fun toGridState(): DeviceGridState =
+ DeviceGridState(size.x, size.y, size.x, InvariantDeviceProfile.TYPE_PHONE, tableName)
+}
+
@SmallTest
@RunWith(AndroidJUnit4::class)
class ValidGridMigrationUnitTest {
companion object {
const val SEED = 1044542
- const val REPEAT_AFTER = 10
+ val REPEAT_AFTER = Point(0, 10)
+ val REPEAT_AFTER_DST = Point(6, 15)
const val TAG = "ValidGridMigrationUnitTest"
+ const val SMALL_TEST_SIZE = 60
+ const val LARGE_TEST_SIZE = 1000
}
private lateinit var context: Context
@@ -57,14 +65,10 @@
context = InstrumentationRegistry.getInstrumentation().targetContext
}
- private fun validate(
- srcItems: List<WorkspaceItem>,
- dstItems: List<WorkspaceItem>,
- destinationSize: Point
- ) {
+ private fun validate(srcGrid: Grid, dstGrid: Grid, resultItems: List<WorkspaceItem>) {
// This returns a map with the number of repeated elements
// ex { calculatorIcon : 6, weatherWidget : 2 }
- val itemsToSet = { it: List<WorkspaceItem> ->
+ val itemsToMap = { it: List<WorkspaceItem> ->
it.filter { it.container != Favorites.CONTAINER_HOTSEAT }
.groupingBy {
when (it.type) {
@@ -77,34 +81,36 @@
}
.eachCount()
}
- for (it in dstItems) {
- assert((it.x in 0..destinationSize.x) && (it.y in 0..destinationSize.y)) {
- "Item outside of the board size. Size = $destinationSize Item = $it"
+ resultItems.forEach {
+ assert((it.x in 0..dstGrid.size.x) && (it.y in 0..dstGrid.size.y)) {
+ "Item outside of the board size. Size = ${dstGrid.size} Item = $it"
}
assert(
- (it.x + it.spanX in 0..destinationSize.x) &&
- (it.y + it.spanY in 0..destinationSize.y)
+ (it.x + it.spanX in 0..dstGrid.size.x) && (it.y + it.spanY in 0..dstGrid.size.y)
) {
- "Item doesn't fit in the grid. Size = $destinationSize Item = $it"
+ "Item doesn't fit in the grid. Size = ${dstGrid.size} Item = $it"
}
}
- assert(itemsToSet(srcItems) == itemsToSet(dstItems)) {
- "The srcItems do not match the dstItems src = $srcItems dst = $dstItems"
+ val srcCountMap = itemsToMap(srcGrid.items)
+ val resultCountMap = itemsToMap(resultItems)
+ val diff = resultCountMap - srcCountMap
+
+ diff.forEach { (k, count) ->
+ assert(count >= 0) { "Source item $k not present on the result" }
}
}
- private fun addItemsToDb(db: SQLiteDatabase, tableName: String, items: List<WorkspaceItem>) {
+ private fun addItemsToDb(db: SQLiteDatabase, grid: Grid) {
LauncherDbUtils.SQLiteTransaction(db).use { transaction ->
- items.forEach { insertIntoDb(tableName, it, transaction.db) }
+ grid.items.forEach { insertIntoDb(grid.tableName, it, transaction.db) }
transaction.commit()
}
}
private fun migrate(
- srcItems: List<WorkspaceItem>,
- srcSize: Point,
- targetSize: Point
+ srcGrid: Grid,
+ dstGrid: Grid,
): List<WorkspaceItem> {
val userSerial = UserCache.INSTANCE[context].getSerialNumberForUser(Process.myUserHandle())
val dbHelper =
@@ -114,46 +120,64 @@
{ UserCache.INSTANCE.get(context).getSerialNumberForUser(it) },
{}
)
- val srcTableName = Favorites.TMP_TABLE
- val dstTableName = Favorites.TABLE_NAME
- Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcTableName)
- addItemsToDb(dbHelper.writableDatabase, srcTableName, srcItems)
+
+ Favorites.addTableToDb(dbHelper.writableDatabase, userSerial, false, srcGrid.tableName)
+
+ addItemsToDb(dbHelper.writableDatabase, srcGrid)
+ addItemsToDb(dbHelper.writableDatabase, dstGrid)
+
LauncherDbUtils.SQLiteTransaction(dbHelper.writableDatabase).use {
GridSizeMigrationUtil.migrate(
dbHelper,
- GridSizeMigrationUtil.DbReader(it.db, srcTableName, context, MockSet(1)),
- GridSizeMigrationUtil.DbReader(it.db, dstTableName, context, MockSet(1)),
- targetSize.x,
- targetSize,
- DeviceGridState(
- srcSize.x,
- srcSize.y,
- srcSize.x,
- InvariantDeviceProfile.TYPE_PHONE,
- srcTableName
- ),
- DeviceGridState(
- targetSize.x,
- targetSize.y,
- targetSize.x,
- InvariantDeviceProfile.TYPE_PHONE,
- dstTableName
- )
+ GridSizeMigrationUtil.DbReader(it.db, srcGrid.tableName, context, MockSet(1)),
+ GridSizeMigrationUtil.DbReader(it.db, dstGrid.tableName, context, MockSet(1)),
+ dstGrid.size.x,
+ dstGrid.size,
+ srcGrid.toGridState(),
+ dstGrid.toGridState()
)
it.commit()
}
- return readDb(dstTableName, dbHelper.readableDatabase)
+ return readDb(dstGrid.tableName, dbHelper.readableDatabase)
}
@Test
fun runTestCase() {
val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
- for (i in 0..50) {
- val testCase = caseGenerator.generateTestCase()
+ for (i in 0..SMALL_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
Log.d(TAG, "Test case = $testCase")
- val srcItemList = generateItemsForTest(testCase, REPEAT_AFTER)
- val dstItemList = migrate(srcItemList, testCase.srcSize, testCase.targetSize)
- validate(srcItemList, dstItemList, testCase.targetSize)
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
+ }
+ }
+
+ @Test
+ fun mergeBoards() {
+ val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
+ for (i in 0..SMALL_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = false)
+ Log.d(TAG, "Test case = $testCase")
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(
+ tableName = Favorites.TABLE_NAME,
+ size = testCase.targetSize,
+ items = generateItemsForTest(testCase.destBoards, REPEAT_AFTER_DST)
+ )
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
}
}
@@ -162,12 +186,18 @@
@Test
fun runExtensiveTestCases() {
val caseGenerator = ValidGridMigrationTestCaseGenerator(Random(SEED.toLong()))
- for (i in 0..1000) {
- val testCase = caseGenerator.generateTestCase()
+ for (i in 0..LARGE_TEST_SIZE) {
+ val testCase = caseGenerator.generateTestCase(isDestEmpty = true)
Log.d(TAG, "Test case = $testCase")
- val srcItemList = generateItemsForTest(testCase, REPEAT_AFTER)
- val dstItemList = migrate(srcItemList, testCase.srcSize, testCase.targetSize)
- validate(srcItemList, dstItemList, testCase.targetSize)
+ val srcGrid =
+ Grid(
+ tableName = Favorites.TMP_TABLE,
+ size = testCase.srcSize,
+ items = generateItemsForTest(testCase.boards, REPEAT_AFTER)
+ )
+ val dstGrid =
+ Grid(tableName = Favorites.TABLE_NAME, size = testCase.targetSize, items = listOf())
+ validate(srcGrid, dstGrid, migrate(srcGrid, dstGrid))
}
}
}