diff options
12 files changed, 388 insertions, 56 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt index d4d966ad2ef7..2312bbd2d7f8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt @@ -22,6 +22,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.communal.nano.CommunalHubState +import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.coroutines.collectLastValue import com.android.systemui.lifecycle.InstantTaskExecutorRule import com.google.common.truth.Truth.assertThat @@ -102,7 +103,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { widgetId = widgetId, provider = provider, rank = rank, - userSerialNumber = userSerialNumber + userSerialNumber = userSerialNumber, ) } assertThat(widgets()) @@ -110,7 +111,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { communalItemRankEntry1, communalWidgetItemEntry1, communalItemRankEntry2, - communalWidgetItemEntry2 + communalWidgetItemEntry2, ) } @@ -129,7 +130,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, - userSerialNumber = userSerialNumber + userSerialNumber = userSerialNumber, ) } @@ -165,7 +166,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { communalItemRankEntry1, communalWidgetItemEntry1, communalItemRankEntry2, - communalWidgetItemEntry2 + communalWidgetItemEntry2, ) communalWidgetDao.deleteWidgetById(communalWidgetItemEntry1.widgetId) @@ -251,6 +252,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = "pk_name/cls_name_4", itemId = 4L, userSerialNumber = 0, + spanY = 3, ) assertThat(widgets()) .containsExactly( @@ -267,6 +269,68 @@ class CommunalWidgetDaoTest : SysuiTestCase() { } @Test + fun addWidget_withDifferentSpanY_readsCorrectValuesInDb() = + testScope.runTest { + val widgets = collectLastValue(communalWidgetDao.getWidgets()) + + // Add widgets with different spanY values + communalWidgetDao.addWidget( + widgetId = 1, + provider = ComponentName("pkg_name", "cls_name_1"), + rank = 0, + userSerialNumber = 0, + spanY = CommunalContentSize.FULL.span, + ) + communalWidgetDao.addWidget( + widgetId = 2, + provider = ComponentName("pkg_name", "cls_name_2"), + rank = 1, + userSerialNumber = 0, + spanY = CommunalContentSize.HALF.span, + ) + communalWidgetDao.addWidget( + widgetId = 3, + provider = ComponentName("pkg_name", "cls_name_3"), + rank = 2, + userSerialNumber = 0, + spanY = CommunalContentSize.THIRD.span, + ) + + // Verify that the widgets have the correct spanY values + assertThat(widgets()) + .containsExactly( + CommunalItemRank(uid = 1L, rank = 0), + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pkg_name/cls_name_1", + itemId = 1L, + userSerialNumber = 0, + spanY = CommunalContentSize.FULL.span, + ), + CommunalItemRank(uid = 2L, rank = 1), + CommunalWidgetItem( + uid = 2L, + widgetId = 2, + componentName = "pkg_name/cls_name_2", + itemId = 2L, + userSerialNumber = 0, + spanY = CommunalContentSize.HALF.span, + ), + CommunalItemRank(uid = 3L, rank = 2), + CommunalWidgetItem( + uid = 3L, + widgetId = 3, + componentName = "pkg_name/cls_name_3", + itemId = 3L, + userSerialNumber = 0, + spanY = CommunalContentSize.THIRD.span, + ), + ) + .inOrder() + } + + @Test fun restoreCommunalHubState() = testScope.runTest { // Set up db @@ -288,6 +352,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = fakeWidget.componentName, itemId = rank.uid, userSerialNumber = fakeWidget.userSerialNumber, + spanY = 3, ) expected[rank] = widget } @@ -343,6 +408,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = widgetInfo1.provider.flattenToString(), itemId = communalItemRankEntry1.uid, userSerialNumber = widgetInfo1.userSerialNumber, + spanY = 3, ) val communalWidgetItemEntry2 = CommunalWidgetItem( @@ -351,6 +417,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = widgetInfo2.provider.flattenToString(), itemId = communalItemRankEntry2.uid, userSerialNumber = widgetInfo2.userSerialNumber, + spanY = 3, ) val communalWidgetItemEntry3 = CommunalWidgetItem( @@ -359,6 +426,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = widgetInfo3.provider.flattenToString(), itemId = communalItemRankEntry3.uid, userSerialNumber = widgetInfo3.userSerialNumber, + spanY = 3, ) val fakeState = CommunalHubState().apply { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt index eba395bdb5a3..596db0767867 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt @@ -117,6 +117,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[0], rank = 0, userSerialNumber = 0, + spanY = 3, ) verify(communalWidgetDao) .addWidget( @@ -124,6 +125,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[1], rank = 1, userSerialNumber = 0, + spanY = 3, ) verify(communalWidgetDao) .addWidget( @@ -131,6 +133,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[2], rank = 2, userSerialNumber = 0, + spanY = 3, ) } @@ -152,6 +155,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = any(), rank = anyInt(), userSerialNumber = anyInt(), + spanY = anyInt(), ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt index 980a5ec8c494..3d30eccc4572 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryImplTest.kt @@ -143,7 +143,8 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { fun communalWidgets_queryWidgetsFromDb() = testScope.runTest { val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1) - val communalWidgetItemEntry = CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0) + val communalWidgetItemEntry = + CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0, 3) fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry) fakeProviders.value = mapOf(1 to providerInfoA) @@ -169,19 +170,15 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0), + CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0), + CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), CommunalItemRank(uid = 3L, rank = 3) to - CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0), + CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0, 3), CommunalItemRank(uid = 4L, rank = 4) to - CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0), - ) - fakeProviders.value = - mapOf( - 1 to providerInfoA, - 2 to providerInfoB, + CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0, 3), ) + fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB) // Expect to see only widget 1 and 2 val communalWidgets by collectLastValue(underTest.communalWidgets) @@ -207,15 +204,11 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0), + CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0), - ) - fakeProviders.value = - mapOf( - 1 to providerInfoA, - 2 to providerInfoB, + CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), ) + fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB) // Expect two widgets val communalWidgets by collectLastValue(underTest.communalWidgets) @@ -235,11 +228,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { ) // Provider info updated for widget 1 - fakeProviders.value = - mapOf( - 1 to providerInfoC, - 2 to providerInfoB, - ) + fakeProviders.value = mapOf(1 to providerInfoC, 2 to providerInfoB) runCurrent() assertThat(communalWidgets) @@ -269,7 +258,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { whenever( communalWidgetHost.allocateIdAndBindWidget( any<ComponentName>(), - any<UserHandle>() + any<UserHandle>(), ) ) .thenReturn(id) @@ -294,7 +283,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { whenever( communalWidgetHost.allocateIdAndBindWidget( any<ComponentName>(), - any<UserHandle>() + any<UserHandle>(), ) ) .thenReturn(id) @@ -303,7 +292,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) verify(communalWidgetDao, never()) - .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt()) + .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt()) verify(appWidgetHost).deleteAppWidgetId(id) // Verify backup not requested @@ -321,7 +310,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { whenever( communalWidgetHost.allocateIdAndBindWidget( any<ComponentName>(), - any<UserHandle>() + any<UserHandle>(), ) ) .thenReturn(id) @@ -332,7 +321,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) verify(communalWidgetDao, never()) - .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt()) + .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt()) verify(appWidgetHost).deleteAppWidgetId(id) // Verify backup not requested @@ -350,7 +339,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { whenever( communalWidgetHost.allocateIdAndBindWidget( any<ComponentName>(), - any<UserHandle>() + any<UserHandle>(), ) ) .thenReturn(id) @@ -650,8 +639,10 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { eq(newWidgetId), componentNameCaptor.capture(), eq(2), - eq(testUserSerialNumber(workProfile)) + eq(testUserSerialNumber(workProfile)), + anyInt(), ) + assertThat(componentNameCaptor.firstValue) .isEqualTo(ComponentName("pk_name", "fake_widget_2")) } @@ -662,9 +653,9 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0), + CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0), + CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), ) // Widget 1 is installed @@ -707,7 +698,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0), + CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3) ) // Widget 1 is pending install @@ -732,7 +723,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { componentName = ComponentName("pk_1", "cls_1"), icon = fakeIcon, user = mainUser, - ), + ) ) // Package for widget 1 finished installing @@ -749,10 +740,23 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() { appWidgetId = 1, providerInfo = providerInfoA, rank = 1, - ), + ) ) } + @Test + fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() = + testScope.runTest { + val widgetId = 1 + val newSpanY = 6 + + underTest.updateWidgetSpanY(widgetId, newSpanY) + runCurrent() + + verify(communalWidgetDao).updateWidgetSpanY(widgetId, newSpanY) + verify(backupManager).dataChanged() + } + private fun setAppWidgetIds(ids: List<Int>) { whenever(appWidgetHost.appWidgetIds).thenReturn(ids.toIntArray()) } diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json new file mode 100644 index 000000000000..c3fb8d4d5ab6 --- /dev/null +++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/4.json @@ -0,0 +1,88 @@ +{ + "formatVersion": 1, + "database": { + "version": 4, + "identityHash": "a49f2f7d25cf12d1baf9a3a3e6243b64", + "entities": [ + { + "tableName": "communal_widget_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `widget_id` INTEGER NOT NULL, `component_name` TEXT NOT NULL, `item_id` INTEGER NOT NULL, `user_serial_number` INTEGER NOT NULL DEFAULT -1, `span_y` INTEGER NOT NULL DEFAULT 3)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "widgetId", + "columnName": "widget_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "componentName", + "columnName": "component_name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "itemId", + "columnName": "item_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userSerialNumber", + "columnName": "user_serial_number", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "-1" + }, + { + "fieldPath": "spanY", + "columnName": "span_y", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "3" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + } + }, + { + "tableName": "communal_item_rank_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `rank` INTEGER NOT NULL DEFAULT 0)", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rank", + "columnName": "rank", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "0" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "uid" + ] + } + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a49f2f7d25cf12d1baf9a3a3e6243b64')" + ] + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt index 8f1854f93fe4..17f4f0c83d6f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt @@ -26,7 +26,7 @@ import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import com.android.systemui.res.R -@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 3) +@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4) abstract class CommunalDatabase : RoomDatabase() { abstract fun communalWidgetDao(): CommunalWidgetDao @@ -43,19 +43,16 @@ abstract class CommunalDatabase : RoomDatabase() { * @param callback An optional callback registered to the database. Only effective when a * new instance is created. */ - fun getInstance( - context: Context, - callback: Callback? = null, - ): CommunalDatabase { + fun getInstance(context: Context, callback: Callback? = null): CommunalDatabase { if (instance == null) { instance = Room.databaseBuilder( context, CommunalDatabase::class.java, - context.resources.getString(R.string.config_communalDatabase) + context.resources.getString(R.string.config_communalDatabase), ) .also { builder -> - builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3) + builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) builder.fallbackToDestructiveMigration(dropAllTables = true) callback?.let { callback -> builder.addCallback(callback) } } @@ -103,5 +100,21 @@ abstract class CommunalDatabase : RoomDatabase() { ) } } + + /** + * This migration adds a span_y column to the communal_widget_table and sets its default + * value to 3. + */ + @VisibleForTesting + val MIGRATION_3_4 = + object : Migration(3, 4) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i(TAG, "Migrating from version 3 to 4") + db.execSQL( + "ALTER TABLE communal_widget_table " + + "ADD COLUMN span_y INTEGER NOT NULL DEFAULT 3" + ) + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt index e33aead11842..f9d2a843c213 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt @@ -40,6 +40,12 @@ data class CommunalWidgetItem( */ @ColumnInfo(name = "user_serial_number", defaultValue = "$USER_SERIAL_NUMBER_UNDEFINED") val userSerialNumber: Int, + + /** + * The vertical span of the widget. Span_Y default value corresponds to + * CommunalContentSize.HALF.span + */ + @ColumnInfo(name = "span_y", defaultValue = "3") val spanY: Int, ) { companion object { /** diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt index 93b86bd10133..5dd4c1cb7f72 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt @@ -25,6 +25,7 @@ import androidx.room.RoomDatabase import androidx.room.Transaction import androidx.sqlite.db.SupportSQLiteDatabase import com.android.systemui.communal.nano.CommunalHubState +import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.widgets.CommunalWidgetHost import com.android.systemui.communal.widgets.CommunalWidgetModule.Companion.DEFAULT_WIDGETS import com.android.systemui.dagger.SysUISingleton @@ -153,14 +154,15 @@ interface CommunalWidgetDao { @Query( "INSERT INTO communal_widget_table" + - "(widget_id, component_name, item_id, user_serial_number) " + - "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber)" + "(widget_id, component_name, item_id, user_serial_number, span_y) " + + "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY)" ) fun insertWidget( widgetId: Int, componentName: String, itemId: Long, userSerialNumber: Int, + spanY: Int = 3, ): Long @Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)") @@ -169,6 +171,9 @@ interface CommunalWidgetDao { @Query("UPDATE communal_item_rank_table SET rank = :order WHERE uid = :itemUid") fun updateItemRank(itemUid: Long, order: Int) + @Query("UPDATE communal_widget_table SET span_y = :spanY WHERE widget_id = :widgetId") + fun updateWidgetSpanY(widgetId: Int, spanY: Int) + @Query("DELETE FROM communal_widget_table") fun clearCommunalWidgetsTable() @Query("DELETE FROM communal_item_rank_table") fun clearCommunalItemRankTable() @@ -189,12 +194,14 @@ interface CommunalWidgetDao { provider: ComponentName, rank: Int? = null, userSerialNumber: Int, + spanY: Int = CommunalContentSize.HALF.span, ): Long { return addWidget( widgetId = widgetId, componentName = provider.flattenToString(), rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) } @@ -204,6 +211,7 @@ interface CommunalWidgetDao { componentName: String, rank: Int? = null, userSerialNumber: Int, + spanY: Int = 3, ): Long { val widgets = getWidgetsNow() @@ -224,6 +232,7 @@ interface CommunalWidgetDao { componentName = componentName, itemId = insertItemRank(newRank), userSerialNumber = userSerialNumber, + spanY = spanY, ) } @@ -246,7 +255,8 @@ interface CommunalWidgetDao { clearCommunalItemRankTable() state.widgets.forEach { - addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber) + val spanY = if (it.spanY != 0) it.spanY else CommunalContentSize.HALF.span + addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, spanY) } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt index 6cdd9fffe077..3312f3cac64b 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt @@ -92,6 +92,14 @@ interface CommunalWidgetRepository { /** Aborts the restore process and removes files from disk if necessary. */ fun abortRestoreWidgets() + + /** + * Update the spanY of a widget in the database. + * + * @param widgetId id of the widget to update. + * @param spanY new spanY value for the widget. + */ + fun updateWidgetSpanY(widgetId: Int, spanY: Int) } @SysUISingleton @@ -118,20 +126,30 @@ constructor( /** Widget metadata from database + matching [AppWidgetProviderInfo] if any. */ private val widgetEntries: Flow<List<CommunalWidgetEntry>> = - combine( - communalWidgetDao.getWidgets(), - communalWidgetHost.appWidgetProviders, - ) { entries, providers -> + combine(communalWidgetDao.getWidgets(), communalWidgetHost.appWidgetProviders) { + entries, + providers -> entries.mapNotNull { (rank, widget) -> CommunalWidgetEntry( appWidgetId = widget.widgetId, componentName = widget.componentName, rank = rank.rank, - providerInfo = providers[widget.widgetId] + providerInfo = providers[widget.widgetId], ) } } + override fun updateWidgetSpanY(widgetId: Int, spanY: Int) { + bgScope.launch { + communalWidgetDao.updateWidgetSpanY(widgetId, spanY) + logger.i({ "Updated spanY of widget $int1 to $int2." }) { + int1 = widgetId + int2 = spanY + } + backupManager.dataChanged() + } + } + @OptIn(ExperimentalCoroutinesApi::class) override val communalWidgets: Flow<List<CommunalWidgetContentModel>> = widgetEntries @@ -197,6 +215,7 @@ constructor( provider = provider, rank = rank, userSerialNumber = userManager.getUserSerialNumber(user.identifier), + spanY = 3, ) backupManager.dataChanged() } else { @@ -325,6 +344,7 @@ constructor( componentName = restoredWidget.componentName rank = restoredWidget.rank userSerialNumber = userManager.getUserSerialNumber(newUser.identifier) + spanY = restoredWidget.spanY } } val newState = CommunalHubState().apply { widgets = newWidgets.toTypedArray() } @@ -383,6 +403,7 @@ constructor( appWidgetId = entry.appWidgetId, providerInfo = entry.providerInfo!!, rank = entry.rank, + spanY = entry.spanY, ) } @@ -400,6 +421,7 @@ constructor( appWidgetId = entry.appWidgetId, providerInfo = entry.providerInfo!!, rank = entry.rank, + spanY = entry.spanY, ) } @@ -412,6 +434,7 @@ constructor( componentName = componentName, icon = session.icon, user = session.user, + spanY = entry.spanY, ) } else { null @@ -423,5 +446,6 @@ constructor( val componentName: String, val rank: Int, var providerInfo: AppWidgetProviderInfo? = null, + var spanY: Int = 3, ) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto index bc14ae1eaff4..7602a7afce4e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto +++ b/packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto @@ -38,5 +38,8 @@ message CommunalHubState { // Serial number of the user associated with the widget. int32 user_serial_number = 4; + + // The vertical span of the widget + int32 span_y = 5; } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt index 63b1a14b3135..bcbc8f65ce36 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalWidgetContentModel.kt @@ -31,6 +31,7 @@ sealed interface CommunalWidgetContentModel { override val appWidgetId: Int, val providerInfo: AppWidgetProviderInfo, override val rank: Int, + val spanY: Int = 3, ) : CommunalWidgetContentModel /** Widget is pending installation */ @@ -40,5 +41,6 @@ sealed interface CommunalWidgetContentModel { val componentName: ComponentName, val icon: Bitmap?, val user: UserHandle, + val spanY: Int = 3, ) : CommunalWidgetContentModel } diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt index ad2550255e29..7d5a334b45ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt @@ -148,6 +148,31 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { ) } + @Test + fun migrate3To4_addSpanYColumn_defaultValuePopulated() { + val databaseV3 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 3) + + val fakeWidgetsV3 = + listOf( + FakeCommunalWidgetItemV3(1, "test_widget_1", 11, 0), + FakeCommunalWidgetItemV3(2, "test_widget_2", 12, 10), + FakeCommunalWidgetItemV3(3, "test_widget_3", 13, 0), + ) + databaseV3.insertWidgetsV3(fakeWidgetsV3) + + databaseV3.verifyWidgetsV3(fakeWidgetsV3) + + val databaseV4 = + migrationTestHelper.runMigrationsAndValidate( + name = DATABASE_NAME, + version = 4, + validateDroppedTables = false, + CommunalDatabase.MIGRATION_3_4, + ) + + databaseV4.verifyWidgetsV4(fakeWidgetsV3.map { it.getV4() }) + } + private fun SupportSQLiteDatabase.insertWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) { widgets.forEach { widget -> execSQL( @@ -157,6 +182,22 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { } } + private fun SupportSQLiteDatabase.insertWidgetsV3(widgets: List<FakeCommunalWidgetItemV3>) { + widgets.forEach { widget -> + execSQL( + "INSERT INTO communal_widget_table(" + + "widget_id, " + + "component_name, " + + "item_id, " + + "user_serial_number) " + + "VALUES(${widget.widgetId}, " + + "'${widget.componentName}', " + + "${widget.itemId}, " + + "${widget.userSerialNumber})" + ) + } + } + private fun SupportSQLiteDatabase.verifyWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) { val cursor = query("SELECT * FROM communal_widget_table") assertThat(cursor.moveToFirst()).isTrue() @@ -193,6 +234,42 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { assertThat(cursor.isAfterLast).isTrue() } + private fun SupportSQLiteDatabase.verifyWidgetsV3(widgets: List<FakeCommunalWidgetItemV3>) { + val cursor = query("SELECT * FROM communal_widget_table") + assertThat(cursor.moveToFirst()).isTrue() + + widgets.forEach { widget -> + assertThat(cursor.getInt(cursor.getColumnIndex("widget_id"))).isEqualTo(widget.widgetId) + assertThat(cursor.getString(cursor.getColumnIndex("component_name"))) + .isEqualTo(widget.componentName) + assertThat(cursor.getInt(cursor.getColumnIndex("item_id"))).isEqualTo(widget.itemId) + assertThat(cursor.getInt(cursor.getColumnIndex("user_serial_number"))) + .isEqualTo(widget.userSerialNumber) + + cursor.moveToNext() + } + assertThat(cursor.isAfterLast).isTrue() + } + + private fun SupportSQLiteDatabase.verifyWidgetsV4(widgets: List<FakeCommunalWidgetItemV4>) { + val cursor = query("SELECT * FROM communal_widget_table") + assertThat(cursor.moveToFirst()).isTrue() + + widgets.forEach { widget -> + assertThat(cursor.getInt(cursor.getColumnIndex("widget_id"))).isEqualTo(widget.widgetId) + assertThat(cursor.getString(cursor.getColumnIndex("component_name"))) + .isEqualTo(widget.componentName) + assertThat(cursor.getInt(cursor.getColumnIndex("item_id"))).isEqualTo(widget.itemId) + assertThat(cursor.getInt(cursor.getColumnIndex("user_serial_number"))) + .isEqualTo(widget.userSerialNumber) + assertThat(cursor.getInt(cursor.getColumnIndex("span_y"))).isEqualTo(widget.spanY) + + cursor.moveToNext() + } + + assertThat(cursor.isAfterLast).isTrue() + } + private fun SupportSQLiteDatabase.insertRanks(ranks: List<FakeCommunalItemRank>) { ranks.forEach { rank -> execSQL("INSERT INTO communal_item_rank_table(rank) VALUES(${rank.rank})") @@ -238,10 +315,27 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { val userSerialNumber: Int, ) - private data class FakeCommunalItemRank( - val rank: Int, + private fun FakeCommunalWidgetItemV3.getV4(): FakeCommunalWidgetItemV4 { + return FakeCommunalWidgetItemV4(widgetId, componentName, itemId, userSerialNumber, 3) + } + + private data class FakeCommunalWidgetItemV3( + val widgetId: Int, + val componentName: String, + val itemId: Int, + val userSerialNumber: Int, + ) + + private data class FakeCommunalWidgetItemV4( + val widgetId: Int, + val componentName: String, + val itemId: Int, + val userSerialNumber: Int, + val spanY: Int, ) + private data class FakeCommunalItemRank(val rank: Int) + companion object { private const val DATABASE_NAME = "communal_db" } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt index 5d7e7c726c6c..1302faaf82ca 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt @@ -31,7 +31,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : provider: ComponentName, user: UserHandle, rank: Int?, - configurator: WidgetConfigurator? + configurator: WidgetConfigurator?, ) { coroutineScope.launch { val id = nextWidgetId++ @@ -93,6 +93,22 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : _communalWidgets.value = fakeDatabase.values.toList() } + override fun updateWidgetSpanY(widgetId: Int, spanY: Int) { + coroutineScope.launch { + fakeDatabase[widgetId]?.let { widget -> + when (widget) { + is CommunalWidgetContentModel.Available -> { + fakeDatabase[widgetId] = widget.copy(spanY = spanY) + } + is CommunalWidgetContentModel.Pending -> { + fakeDatabase[widgetId] = widget.copy(spanY = spanY) + } + } + _communalWidgets.value = fakeDatabase.values.toList() + } + } + } + override fun restoreWidgets(oldToNewWidgetIdMap: Map<Int, Int>) {} override fun abortRestoreWidgets() {} |