diff options
| author | 2024-12-02 17:45:32 +0000 | |
|---|---|---|
| committer | 2024-12-02 17:45:32 +0000 | |
| commit | 0ae597a9d8d8abc9517f0d39ed1a9a1ed42dc66e (patch) | |
| tree | 0cea4c80fa259bc23694028adb2cdeae57f0acc7 | |
| parent | d9f7109671e30cec35de60569cc145e7cb23169d (diff) | |
| parent | cefca220fb1f57ace39abe133899f5b9115421f1 (diff) | |
Merge changes Id312bbfb,I147e1ba5 into main
* changes:
Update communal hub to use responsive grid
Update communal db for responsive grid
23 files changed, 974 insertions, 256 deletions
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 02e7b5f96866..e2f28faa721a 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1204,6 +1204,13 @@ flag { } flag { + name: "communal_responsive_grid" + namespace: "systemui" + description: "Enables responsive grid on glanceable hub" + bug: "378171351" +} + +flag { name: "communal_standalone_support" namespace: "systemui" description: "Support communal features without a dock" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index f3cf521f5fbc..573e5ca5e2d5 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -66,6 +66,7 @@ import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridItemSpan +import androidx.compose.foundation.lazy.grid.LazyGridScope import androidx.compose.foundation.lazy.grid.LazyGridState import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid import androidx.compose.foundation.lazy.grid.itemsIndexed @@ -169,6 +170,7 @@ import com.android.compose.modifiers.thenIf import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.internal.R.dimen.system_app_widget_background_radius import com.android.systemui.Flags +import com.android.systemui.Flags.communalResponsiveGrid import com.android.systemui.Flags.communalTimerFlickerFix import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.communal.domain.model.CommunalContentModel @@ -194,7 +196,6 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.statusbar.phone.SystemUIDialogFactory import kotlin.math.max import kotlin.math.min -import kotlin.math.roundToInt import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -693,7 +694,12 @@ private fun ResizableItemFrameWrapper( onResize = onResize, minHeightPx = minHeightPx, maxHeightPx = maxHeightPx, - resizeMultiple = CommunalContentSize.HALF.span, + resizeMultiple = + if (communalResponsiveGrid()) { + 1 + } else { + CommunalContentSize.FixedSize.HALF.span + }, ) { content(Modifier) } @@ -701,14 +707,22 @@ private fun ResizableItemFrameWrapper( } @Composable -fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): WidgetSizeInfo { +fun calculateWidgetSize( + cellHeight: Dp?, + availableHeight: Dp?, + item: CommunalContentModel, + isResizable: Boolean, +): WidgetSizeInfo { val density = LocalDensity.current + val minHeight = cellHeight ?: CommunalContentSize.FixedSize.HALF.dp() + val maxHeight = availableHeight ?: CommunalContentSize.FixedSize.FULL.dp() + return if (isResizable && item is CommunalContentModel.WidgetContent.Widget) { with(density) { val minHeightPx = (min(item.providerInfo.minResizeHeight, item.providerInfo.minHeight) - .coerceAtLeast(CommunalContentSize.HALF.dp().toPx().roundToInt())) + .coerceAtLeast(minHeight.roundToPx())) val maxHeightPx = (if (item.providerInfo.maxResizeHeight > 0) { @@ -716,7 +730,7 @@ fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): Widge } else { Int.MAX_VALUE }) - .coerceIn(minHeightPx, CommunalContentSize.FULL.dp().toPx().roundToInt()) + .coerceIn(minHeightPx, maxHeight.roundToPx()) WidgetSizeInfo(minHeightPx, maxHeightPx) } @@ -725,6 +739,37 @@ fun calculateWidgetSize(item: CommunalContentModel, isResizable: Boolean): Widge } } +@Composable +private fun HorizontalGridWrapper( + contentPadding: PaddingValues, + gridState: LazyGridState, + modifier: Modifier = Modifier, + content: LazyGridScope.(sizeInfo: SizeInfo?) -> Unit, +) { + if (communalResponsiveGrid()) { + ResponsiveLazyHorizontalGrid( + cellAspectRatio = 1.5f, + modifier = modifier, + state = gridState, + minContentPadding = contentPadding, + minHorizontalArrangement = Dimensions.ItemSpacing, + minVerticalArrangement = Dimensions.ItemSpacing, + content = content, + ) + } else { + LazyHorizontalGrid( + modifier = modifier, + state = gridState, + rows = GridCells.Fixed(CommunalContentSize.FixedSize.FULL.span), + contentPadding = contentPadding, + horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing), + verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing), + ) { + content(null) + } + } +} + @OptIn(ExperimentalFoundationApi::class) @Composable private fun BoxScope.CommunalHubLazyGrid( @@ -778,28 +823,32 @@ private fun BoxScope.CommunalHubLazyGrid( // Since the grid has its own listener for in-grid drag events, we use a separate element // for android drag events. Box(Modifier.fillMaxSize().dragAndDropTarget(dragAndDropTargetState)) {} + } else if (communalResponsiveGrid()) { + gridModifier = gridModifier.fillMaxSize() } else { gridModifier = gridModifier.height(hubDimensions.GridHeight) } - val itemArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing) - LazyHorizontalGrid( + HorizontalGridWrapper( modifier = gridModifier, - state = gridState, - rows = GridCells.Fixed(CommunalContentSize.FULL.span), + gridState = gridState, contentPadding = contentPadding, - horizontalArrangement = itemArrangement, - verticalArrangement = itemArrangement, - ) { + ) { sizeInfo -> itemsIndexed( items = list, key = { _, item -> item.key }, contentType = { _, item -> item.key }, - span = { _, item -> GridItemSpan(item.size.span) }, + span = { _, item -> GridItemSpan(item.getSpanOrMax(sizeInfo?.gridSize?.height)) }, ) { index, item -> - val size = SizeF(Dimensions.CardWidth.value, item.size.dp().value) + val currentItemSpan = item.getSpanOrMax(sizeInfo?.gridSize?.height) + val dpSize = + if (sizeInfo != null) { + DpSize(sizeInfo.cellSize.width, sizeInfo.calculateHeight(currentItemSpan)) + } else { + DpSize(Dimensions.CardWidth, (item.size as CommunalContentSize.FixedSize).dp()) + } + val size = SizeF(dpSize.width.value, dpSize.height.value) val selected = item.key == selectedKey.value - val dpSize = DpSize(size.width.dp, size.height.dp) val isResizable = if (item is CommunalContentModel.WidgetContent.Widget) { item.providerInfo.resizeMode and AppWidgetProviderInfo.RESIZE_VERTICAL != 0 @@ -809,7 +858,7 @@ private fun BoxScope.CommunalHubLazyGrid( val resizeableItemFrameViewModel = rememberViewModel( - key = item.size.span, + key = currentItemSpan, traceName = "ResizeableItemFrame.viewModel.$index", ) { ResizeableItemFrameViewModel() @@ -822,13 +871,23 @@ private fun BoxScope.CommunalHubLazyGrid( animationSpec = spring(stiffness = Spring.StiffnessMediumLow), label = "Widget resizing outline alpha", ) - val widgetSizeInfo = calculateWidgetSize(item, isResizable) + + val widgetSizeInfo = + calculateWidgetSize( + cellHeight = sizeInfo?.cellSize?.height, + availableHeight = sizeInfo?.availableHeight, + item = item, + isResizable = isResizable, + ) ResizableItemFrameWrapper( key = item.key, - currentSpan = GridItemSpan(item.size.span), + currentSpan = GridItemSpan(currentItemSpan), gridState = gridState, gridContentPadding = contentPadding, - verticalArrangement = itemArrangement, + verticalArrangement = + Arrangement.spacedBy( + sizeInfo?.verticalArrangement ?: Dimensions.ItemSpacing + ), enabled = selected, alpha = { outlineAlpha }, modifier = @@ -1686,11 +1745,11 @@ private fun beforeContentPadding(paddingValues: PaddingValues): ContentPaddingIn } } -private fun CommunalContentSize.dp(): Dp { +private fun CommunalContentSize.FixedSize.dp(): Dp { return when (this) { - CommunalContentSize.FULL -> Dimensions.CardHeightFull - CommunalContentSize.HALF -> Dimensions.CardHeightHalf - CommunalContentSize.THIRD -> Dimensions.CardHeightThird + CommunalContentSize.FixedSize.FULL -> Dimensions.CardHeightFull + CommunalContentSize.FixedSize.HALF -> Dimensions.CardHeightHalf + CommunalContentSize.FixedSize.THIRD -> Dimensions.CardHeightThird } } @@ -1709,7 +1768,10 @@ class Dimensions(val context: Context, val config: Configuration) { val GridTopSpacing: Dp get() { val result = - if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) { + if ( + communalResponsiveGrid() || + config.orientation == Configuration.ORIENTATION_LANDSCAPE + ) { 114.dp } else { val windowMetrics = @@ -1729,7 +1791,7 @@ class Dimensions(val context: Context, val config: Configuration) { get() = 530.adjustedDp val ItemSpacing - get() = 50.adjustedDp + get() = if (communalResponsiveGrid()) 32.adjustedDp else 50.adjustedDp val CardHeightHalf get() = (CardHeightFull - ItemSpacing) / 2 @@ -1771,6 +1833,13 @@ class Dimensions(val context: Context, val config: Configuration) { data class WidgetSizeInfo(val minHeightPx: Int, val maxHeightPx: Int) +private fun CommunalContentModel.getSpanOrMax(maxSpan: Int?) = + if (maxSpan != null) { + size.span.coerceAtMost(maxSpan) + } else { + size.span + } + private object Colors { val DisabledColorFilter by lazy { disabledColorMatrix() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt index e3310780afd7..3642127d0823 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt @@ -147,9 +147,9 @@ fun ResponsiveLazyHorizontalGrid( SizeInfo( cellSize = finalSize, contentPadding = finalContentPadding, - horizontalArrangement = minHorizontalArrangement, verticalArrangement = minVerticalArrangement, maxHeight = maxHeight, + gridSize = gridSize, ) ) } @@ -176,16 +176,15 @@ private fun calculateClosestSize(maxWidth: Dp, maxHeight: Dp, aspectRatio: Float * Provides size info of the responsive grid, since the size is dynamic. * * @property cellSize The size of each cell in the grid. - * @property contentPadding The final content padding of the grid. - * @property horizontalArrangement The space between columns in the grid. * @property verticalArrangement The space between rows in the grid. + * @property gridSize The size of the grid, in cell units. * @property availableHeight The maximum height an item in the grid may occupy. */ data class SizeInfo( val cellSize: DpSize, - val contentPadding: PaddingValues, - val horizontalArrangement: Dp, val verticalArrangement: Dp, + val gridSize: IntSize, + private val contentPadding: PaddingValues, private val maxHeight: Dp, ) { val availableHeight: Dp @@ -193,6 +192,11 @@ data class SizeInfo( maxHeight - contentPadding.calculateBottomPadding() - contentPadding.calculateTopPadding() + + /** Calculates the height in dp of a certain number of rows. */ + fun calculateHeight(numRows: Int): Dp { + return numRows * cellSize.height + (numRows - 1) * verticalArrangement + } } @Composable 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 596db0767867..f1c58a2aeac9 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 @@ -24,6 +24,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.db.DefaultWidgetPopulation.SkipReason.RESTORED_FROM_BACKUP +import com.android.systemui.communal.shared.model.SpanValue import com.android.systemui.communal.widgets.CommunalWidgetHost import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope @@ -117,7 +118,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[0], rank = 0, userSerialNumber = 0, - spanY = 3, + spanY = SpanValue.Fixed(3), ) verify(communalWidgetDao) .addWidget( @@ -125,7 +126,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[1], rank = 1, userSerialNumber = 0, - spanY = 3, + spanY = SpanValue.Fixed(3), ) verify(communalWidgetDao) .addWidget( @@ -133,7 +134,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = defaultWidgets[2], rank = 2, userSerialNumber = 0, - spanY = 3, + spanY = SpanValue.Fixed(3), ) } @@ -155,7 +156,7 @@ class DefaultWidgetPopulationTest : SysuiTestCase() { componentName = any(), rank = anyInt(), userSerialNumber = anyInt(), - spanY = anyInt(), + spanY = any(), ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt index 55d7d08e8519..335e39902983 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt @@ -24,11 +24,15 @@ import android.content.ComponentName import android.content.applicationContext import android.graphics.Bitmap import android.os.UserHandle +import android.os.UserManager import android.os.userManager +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags -import androidx.test.ext.junit.runners.AndroidJUnit4 +import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest +import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING +import com.android.systemui.Flags.communalResponsiveGrid import com.android.systemui.SysuiTestCase import com.android.systemui.common.data.repository.fakePackageChangeRepository import com.android.systemui.common.shared.model.PackageInstallSession @@ -40,11 +44,15 @@ import com.android.systemui.communal.data.db.defaultWidgetPopulation import com.android.systemui.communal.nano.CommunalHubState import com.android.systemui.communal.proto.toByteArray import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.SpanValue import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.CommunalWidgetHost import com.android.systemui.communal.widgets.widgetConfiguratorFail import com.android.systemui.communal.widgets.widgetConfiguratorSuccess -import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.collectLastValue +import com.android.systemui.kosmos.runCurrent +import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.LogBuffer @@ -52,48 +60,55 @@ import com.android.systemui.log.logcatLogBuffer import com.android.systemui.res.R import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt -import org.mockito.Mock import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.whenever +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest -@RunWith(AndroidJUnit4::class) -class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { - @Mock private lateinit var appWidgetHost: CommunalAppWidgetHost - @Mock private lateinit var providerInfoA: AppWidgetProviderInfo - @Mock private lateinit var providerInfoB: AppWidgetProviderInfo - @Mock private lateinit var providerInfoC: AppWidgetProviderInfo - @Mock private lateinit var communalWidgetHost: CommunalWidgetHost - @Mock private lateinit var communalWidgetDao: CommunalWidgetDao - @Mock private lateinit var backupManager: BackupManager +@RunWith(ParameterizedAndroidJunit4::class) +class CommunalWidgetRepositoryLocalImplTest(flags: FlagsParameterization) : SysuiTestCase() { + private val kosmos = testKosmos() + + private val appWidgetHost = mock<CommunalAppWidgetHost>() + private val providerInfoA = mock<AppWidgetProviderInfo>() + private val providerInfoB = mock<AppWidgetProviderInfo>() + private val providerInfoC = mock<AppWidgetProviderInfo>() private val communalHubStateCaptor = argumentCaptor<CommunalHubState>() private val componentNameCaptor = argumentCaptor<ComponentName>() - private lateinit var backupUtils: CommunalBackupUtils - private lateinit var logBuffer: LogBuffer - private lateinit var fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>> - private lateinit var fakeProviders: MutableStateFlow<Map<Int, AppWidgetProviderInfo?>> + private val Kosmos.communalWidgetHost by + Kosmos.Fixture { + mock<CommunalWidgetHost> { on { appWidgetProviders } doReturn fakeProviders } + } + private val Kosmos.communalWidgetDao by + Kosmos.Fixture { mock<CommunalWidgetDao> { on { getWidgets() } doReturn fakeWidgets } } + + private val Kosmos.backupManager by Kosmos.Fixture { mock<BackupManager>() } - private val kosmos = testKosmos() - private val testScope = kosmos.testScope - private val packageChangeRepository = kosmos.fakePackageChangeRepository - private val userManager = kosmos.userManager + private val Kosmos.backupUtils: CommunalBackupUtils by + Kosmos.Fixture { CommunalBackupUtils(applicationContext) } + + private val Kosmos.logBuffer: LogBuffer by + Kosmos.Fixture { logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest") } + + private val Kosmos.fakeWidgets: MutableStateFlow<Map<CommunalItemRank, CommunalWidgetItem>> by + Kosmos.Fixture { MutableStateFlow(emptyMap()) } + + private val Kosmos.fakeProviders: MutableStateFlow<Map<Int, AppWidgetProviderInfo?>> by + Kosmos.Fixture { MutableStateFlow(emptyMap()) } private val mainUser = UserHandle(0) private val workProfile = UserHandle(10) @@ -105,48 +120,49 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { "com.android.fake/WidgetProviderC", ) - private lateinit var underTest: CommunalWidgetRepositoryLocalImpl - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - fakeWidgets = MutableStateFlow(emptyMap()) - fakeProviders = MutableStateFlow(emptyMap()) - logBuffer = logcatLogBuffer(name = "CommunalWidgetRepoLocalImplTest") - backupUtils = CommunalBackupUtils(kosmos.applicationContext) - - setAppWidgetIds(emptyList()) - - overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray()) - - whenever(communalWidgetDao.getWidgets()).thenReturn(fakeWidgets) - whenever(communalWidgetHost.appWidgetProviders).thenReturn(fakeProviders) - whenever(userManager.mainUser).thenReturn(mainUser) - - restoreUser(mainUser) - - underTest = + private val Kosmos.underTest by + Kosmos.Fixture { CommunalWidgetRepositoryLocalImpl( appWidgetHost, testScope.backgroundScope, - kosmos.testDispatcher, + testDispatcher, communalWidgetHost, communalWidgetDao, logBuffer, backupManager, backupUtils, - packageChangeRepository, + fakePackageChangeRepository, userManager, - kosmos.defaultWidgetPopulation, + defaultWidgetPopulation, ) + } + + init { + mSetFlagsRule.setFlagsParameterization(flags) + } + + @Before + fun setUp() { + kosmos.userManager = mock<UserManager> { on { mainUser } doReturn mainUser } + setAppWidgetIds(emptyList()) + overrideResource(R.array.config_communalWidgetAllowlist, fakeAllowlist.toTypedArray()) + restoreUser(mainUser) } @Test fun communalWidgets_queryWidgetsFromDb() = - testScope.runTest { + kosmos.runTest { val communalItemRankEntry = CommunalItemRank(uid = 1L, rank = 1) val communalWidgetItemEntry = - CommunalWidgetItem(uid = 1L, 1, "pk_name/cls_name", 1L, 0, 3) + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pk_name/cls_name", + itemId = 1L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ) fakeWidgets.value = mapOf(communalItemRankEntry to communalWidgetItemEntry) fakeProviders.value = mapOf(1 to providerInfoA) @@ -158,7 +174,12 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { appWidgetId = communalWidgetItemEntry.widgetId, providerInfo = providerInfoA, rank = communalItemRankEntry.rank, - spanY = communalWidgetItemEntry.spanY, + spanY = + if (communalResponsiveGrid()) { + communalWidgetItemEntry.spanYNew + } else { + communalWidgetItemEntry.spanY + }, ) ) @@ -168,18 +189,50 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun communalWidgets_widgetsWithoutMatchingProvidersAreSkipped() = - testScope.runTest { + kosmos.runTest { // Set up 4 widgets, but widget 3 and 4 don't have matching providers fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pk_1/cls_1", + itemId = 1L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), + CommunalWidgetItem( + uid = 2L, + widgetId = 2, + componentName = "pk_2/cls_2", + itemId = 2L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), CommunalItemRank(uid = 3L, rank = 3) to - CommunalWidgetItem(uid = 3L, 3, "pk_3/cls_3", 3L, 0, 3), + CommunalWidgetItem( + uid = 3L, + widgetId = 3, + componentName = "pk_3/cls_3", + itemId = 3L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), CommunalItemRank(uid = 4L, rank = 4) to - CommunalWidgetItem(uid = 4L, 4, "pk_4/cls_4", 4L, 0, 3), + CommunalWidgetItem( + uid = 4L, + widgetId = 4, + componentName = "pk_4/cls_4", + itemId = 4L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), ) fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB) @@ -191,27 +244,43 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { appWidgetId = 1, providerInfo = providerInfoA, rank = 1, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), CommunalWidgetContentModel.Available( appWidgetId = 2, providerInfo = providerInfoB, rank = 2, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), ) } @Test fun communalWidgets_updatedWhenProvidersUpdate() = - testScope.runTest { + kosmos.runTest { // Set up widgets and providers fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pk_1/cls_1", + itemId = 1L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), + CommunalWidgetItem( + uid = 2L, + widgetId = 2, + componentName = "pk_2/cls_2", + itemId = 2L, + userSerialNumber = 0, + spanY = 6, + spanYNew = 2, + ), ) fakeProviders.value = mapOf(1 to providerInfoA, 2 to providerInfoB) @@ -224,13 +293,13 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { appWidgetId = 1, providerInfo = providerInfoA, rank = 1, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), CommunalWidgetContentModel.Available( appWidgetId = 2, providerInfo = providerInfoB, rank = 2, - spanY = 3, + spanY = if (communalResponsiveGrid()) 2 else 6, ), ) @@ -245,20 +314,20 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { // Verify that provider info updated providerInfo = providerInfoC, rank = 1, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), CommunalWidgetContentModel.Available( appWidgetId = 2, providerInfo = providerInfoB, rank = 2, - spanY = 3, + spanY = if (communalResponsiveGrid()) 2 else 6, ), ) } @Test fun addWidget_allocateId_bindWidget_andAddToDb() = - testScope.runTest { + kosmos.runTest { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val rank = 1 @@ -275,7 +344,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) - verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser)) + verify(communalWidgetDao) + .addWidget(id, provider, rank, testUserSerialNumber(mainUser), SpanValue.Fixed(3)) // Verify backup requested verify(backupManager).dataChanged() @@ -283,7 +353,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun addWidget_configurationFails_doNotAddWidgetToDb() = - testScope.runTest { + kosmos.runTest { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val rank = 1 @@ -301,7 +371,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) verify(communalWidgetDao, never()) - .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt()) + .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), any()) verify(appWidgetHost).deleteAppWidgetId(id) // Verify backup not requested @@ -310,7 +380,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun addWidget_configurationThrowsError_doNotAddWidgetToDb() = - testScope.runTest { + kosmos.runTest { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val rank = 1 @@ -330,7 +400,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) verify(communalWidgetDao, never()) - .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), anyInt()) + .addWidget(anyInt(), any<ComponentName>(), anyInt(), anyInt(), any()) verify(appWidgetHost).deleteAppWidgetId(id) // Verify backup not requested @@ -339,7 +409,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun addWidget_configurationNotRequired_doesNotConfigure_addWidgetToDb() = - testScope.runTest { + kosmos.runTest { val provider = ComponentName("pkg_name", "cls_name") val id = 1 val rank = 1 @@ -356,7 +426,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { runCurrent() verify(communalWidgetHost).allocateIdAndBindWidget(provider, mainUser) - verify(communalWidgetDao).addWidget(id, provider, rank, testUserSerialNumber(mainUser)) + verify(communalWidgetDao) + .addWidget(id, provider, rank, testUserSerialNumber(mainUser), SpanValue.Fixed(3)) // Verify backup requested verify(backupManager).dataChanged() @@ -364,7 +435,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun deleteWidget_deleteFromDbTrue_alsoDeleteFromHost() = - testScope.runTest { + kosmos.runTest { val id = 1 whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(true) underTest.deleteWidget(id) @@ -379,7 +450,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun deleteWidget_deleteFromDbFalse_doesNotDeleteFromHost() = - testScope.runTest { + kosmos.runTest { val id = 1 whenever(communalWidgetDao.deleteWidgetById(eq(id))).thenReturn(false) underTest.deleteWidget(id) @@ -394,7 +465,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun reorderWidgets_queryDb() = - testScope.runTest { + kosmos.runTest { val widgetIdToRankMap = mapOf(104 to 1, 103 to 2, 101 to 3) underTest.updateWidgetOrder(widgetIdToRankMap) runCurrent() @@ -407,7 +478,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_deleteStateFileIfRestoreFails() = - testScope.runTest { + kosmos.runTest { // Write a state file that is invalid, and verify it is written backupUtils.writeBytesToDisk(byteArrayOf(1, 2, 3, 4, 5, 6)) assertThat(backupUtils.fileExists()).isTrue() @@ -422,7 +493,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_deleteStateFileAfterWidgetsRestored() = - testScope.runTest { + kosmos.runTest { // Write a state file, and verify it is written backupUtils.writeBytesToDisk(fakeState.toByteArray()) assertThat(backupUtils.fileExists()).isTrue() @@ -443,7 +514,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_restoredWidgetsNotRegisteredWithHostAreSkipped() = - testScope.runTest { + kosmos.runTest { // Write fake state to file backupUtils.writeBytesToDisk(fakeState.toByteArray()) @@ -470,7 +541,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_registeredWidgetsNotRestoredAreRemoved() = - testScope.runTest { + kosmos.runTest { // Write fake state to file backupUtils.writeBytesToDisk(fakeState.toByteArray()) @@ -504,7 +575,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_onlySomeWidgetsGotNewIds() = - testScope.runTest { + kosmos.runTest { // Write fake state to file backupUtils.writeBytesToDisk(fakeState.toByteArray()) @@ -536,7 +607,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_undefinedUser_restoredAsMain() = - testScope.runTest { + kosmos.runTest { // Write two widgets to file, both of which have user serial number undefined. val fakeState = CommunalHubState().apply { @@ -584,7 +655,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_workProfileNotRestored_widgetSkipped() = - testScope.runTest { + kosmos.runTest { // Write fake state to file backupUtils.writeBytesToDisk(fakeStateWithWorkProfile.toByteArray()) @@ -610,7 +681,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun restoreWidgets_workProfileRestored_manuallyBindWidget() = - testScope.runTest { + kosmos.runTest { // Write fake state to file backupUtils.writeBytesToDisk(fakeStateWithWorkProfile.toByteArray()) @@ -649,7 +720,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { componentNameCaptor.capture(), eq(2), eq(testUserSerialNumber(workProfile)), - anyInt(), + any(), ) assertThat(componentNameCaptor.firstValue) @@ -658,13 +729,29 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { @Test fun pendingWidgets() = - testScope.runTest { + kosmos.runTest { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3), + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pk_1/cls_1", + itemId = 1L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), CommunalItemRank(uid = 2L, rank = 2) to - CommunalWidgetItem(uid = 2L, 2, "pk_2/cls_2", 2L, 0, 3), + CommunalWidgetItem( + uid = 2L, + widgetId = 2, + componentName = "pk_2/cls_2", + itemId = 2L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ), ) // Widget 1 is installed @@ -672,7 +759,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { // Widget 2 is pending install val fakeIcon = mock<Bitmap>() - packageChangeRepository.setInstallSessions( + fakePackageChangeRepository.setInstallSessions( listOf( PackageInstallSession( sessionId = 1, @@ -690,7 +777,7 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { appWidgetId = 1, providerInfo = providerInfoA, rank = 1, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), CommunalWidgetContentModel.Pending( appWidgetId = 2, @@ -698,23 +785,31 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { componentName = ComponentName("pk_2", "cls_2"), icon = fakeIcon, user = mainUser, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ), ) } @Test fun pendingWidgets_pendingWidgetBecomesAvailableAfterInstall() = - testScope.runTest { + kosmos.runTest { fakeWidgets.value = mapOf( CommunalItemRank(uid = 1L, rank = 1) to - CommunalWidgetItem(uid = 1L, 1, "pk_1/cls_1", 1L, 0, 3) + CommunalWidgetItem( + uid = 1L, + widgetId = 1, + componentName = "pk_1/cls_1", + itemId = 1L, + userSerialNumber = 0, + spanY = 3, + spanYNew = 1, + ) ) // Widget 1 is pending install val fakeIcon = mock<Bitmap>() - packageChangeRepository.setInstallSessions( + fakePackageChangeRepository.setInstallSessions( listOf( PackageInstallSession( sessionId = 1, @@ -734,12 +829,12 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { componentName = ComponentName("pk_1", "cls_1"), icon = fakeIcon, user = mainUser, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ) ) // Package for widget 1 finished installing - packageChangeRepository.setInstallSessions(emptyList()) + fakePackageChangeRepository.setInstallSessions(emptyList()) // Provider info for widget 1 becomes available fakeProviders.value = mapOf(1 to providerInfoA) @@ -752,15 +847,32 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { appWidgetId = 1, providerInfo = providerInfoA, rank = 1, - spanY = 3, + spanY = if (communalResponsiveGrid()) 1 else 3, ) ) } @Test @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING) - fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() = - testScope.runTest { + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) + fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup_fixed() = + kosmos.runTest { + val widgetId = 1 + val newSpanY = 6 + val widgetIdToRankMap = emptyMap<Int, Int>() + + underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap) + runCurrent() + + verify(communalWidgetDao) + .resizeWidget(widgetId, SpanValue.Fixed(newSpanY), widgetIdToRankMap) + verify(backupManager).dataChanged() + } + + @Test + @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID) + fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup_responsive() = + kosmos.runTest { val widgetId = 1 val newSpanY = 6 val widgetIdToRankMap = emptyMap<Int, Int>() @@ -768,7 +880,8 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap) runCurrent() - verify(communalWidgetDao).resizeWidget(widgetId, newSpanY, widgetIdToRankMap) + verify(communalWidgetDao) + .resizeWidget(widgetId, SpanValue.Responsive(newSpanY), widgetIdToRankMap) verify(backupManager).dataChanged() } @@ -784,13 +897,19 @@ class CommunalWidgetRepositoryLocalImplTest : SysuiTestCase() { } private fun restoreUser(user: UserHandle) { - whenever(backupManager.getUserForAncestralSerialNumber(user.identifier.toLong())) + whenever(kosmos.backupManager.getUserForAncestralSerialNumber(user.identifier.toLong())) .thenReturn(user) - whenever(userManager.getUserSerialNumber(user.identifier)) + whenever(kosmos.userManager.getUserSerialNumber(user.identifier)) .thenReturn(testUserSerialNumber(user)) } - private companion object { + companion object { + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID) + } + val PROVIDER_INFO_REQUIRES_CONFIGURATION = AppWidgetProviderInfo().apply { configure = ComponentName("test.pkg", "test.cmp") } val PROVIDER_INFO_CONFIGURATION_OPTIONAL = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index 611a61a6282c..b9e646fee98f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -24,14 +24,16 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.os.UserManager import android.os.userManager +import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings import android.provider.Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED import android.widget.RemoteViews -import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING import com.android.systemui.SysuiTestCase import com.android.systemui.broadcast.broadcastDispatcher @@ -96,6 +98,8 @@ import org.mockito.Mock import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters /** * This class of test cases assume that communal is enabled. For disabled cases, see @@ -103,8 +107,8 @@ import org.mockito.MockitoAnnotations */ @SmallTest @OptIn(ExperimentalCoroutinesApi::class) -@RunWith(AndroidJUnit4::class) -class CommunalInteractorTest : SysuiTestCase() { +@RunWith(ParameterizedAndroidJunit4::class) +class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() { @Mock private lateinit var mainUser: UserInfo @Mock private lateinit var secondaryUser: UserInfo @@ -129,6 +133,10 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var underTest: CommunalInteractor + init { + mSetFlagsRule.setFlagsParameterization(flags) + } + @Before fun setUp() { MockitoAnnotations.initMocks(this) @@ -262,71 +270,84 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(widgetContent!![2].appWidgetId).isEqualTo(3) } + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspaceDynamicSizing_oneCard_fullSize() = testSmartspaceDynamicSizing( totalTargets = 1, - expectedSizes = listOf(CommunalContentSize.FULL), + expectedSizes = listOf(CommunalContentSize.FixedSize.FULL), ) + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspace_dynamicSizing_twoCards_halfSize() = testSmartspaceDynamicSizing( totalTargets = 2, - expectedSizes = listOf(CommunalContentSize.HALF, CommunalContentSize.HALF), + expectedSizes = + listOf(CommunalContentSize.FixedSize.HALF, CommunalContentSize.FixedSize.HALF), ) + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspace_dynamicSizing_threeCards_thirdSize() = testSmartspaceDynamicSizing( totalTargets = 3, expectedSizes = listOf( - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, ), ) + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspace_dynamicSizing_fourCards_threeThirdSizeAndOneFullSize() = testSmartspaceDynamicSizing( totalTargets = 4, expectedSizes = listOf( - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.FULL, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.FULL, ), ) + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspace_dynamicSizing_fiveCards_threeThirdAndTwoHalfSize() = testSmartspaceDynamicSizing( totalTargets = 5, expectedSizes = listOf( - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.HALF, - CommunalContentSize.HALF, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.HALF, + CommunalContentSize.FixedSize.HALF, ), ) + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun smartspace_dynamicSizing_sixCards_allThirdSize() = testSmartspaceDynamicSizing( totalTargets = 6, expectedSizes = listOf( - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, - CommunalContentSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, + CommunalContentSize.FixedSize.THIRD, ), ) @@ -383,7 +404,9 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(umoContent?.size).isEqualTo(0) } + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun ongoing_shouldOrderAndSizeByTimestamp() = testScope.runTest { // Keyguard showing, and tutorial completed. @@ -410,15 +433,15 @@ class CommunalInteractorTest : SysuiTestCase() { assertThat(ongoingContent?.size).isEqualTo(4) assertThat(ongoingContent?.get(0)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("timer3")) - assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.HALF) + assertThat(ongoingContent?.get(0)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) assertThat(ongoingContent?.get(1)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("timer2")) - assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.HALF) + assertThat(ongoingContent?.get(1)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) assertThat(ongoingContent?.get(2)?.key).isEqualTo(CommunalContentModel.KEY.umo()) - assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.HALF) + assertThat(ongoingContent?.get(2)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) assertThat(ongoingContent?.get(3)?.key) .isEqualTo(CommunalContentModel.KEY.smartspace("timer1")) - assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.HALF) + assertThat(ongoingContent?.get(3)?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) } @Test @@ -1082,6 +1105,7 @@ class CommunalInteractorTest : SysuiTestCase() { @Test @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING) + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun resizeWidget_withoutUpdatingOrder() = testScope.runTest { val userInfos = listOf(MAIN_USER_INFO) @@ -1094,45 +1118,97 @@ class CommunalInteractorTest : SysuiTestCase() { appWidgetId = 1, userId = MAIN_USER_INFO.id, rank = 0, - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, ) widgetRepository.addWidget( appWidgetId = 2, userId = MAIN_USER_INFO.id, rank = 1, - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, ) widgetRepository.addWidget( appWidgetId = 3, userId = MAIN_USER_INFO.id, rank = 2, - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, ) val widgetContent by collectLastValue(underTest.widgetContent) assertThat(widgetContent?.map { it.appWidgetId to it.size }) .containsExactly( - 1 to CommunalContentSize.HALF, - 2 to CommunalContentSize.HALF, - 3 to CommunalContentSize.HALF, + 1 to CommunalContentSize.FixedSize.HALF, + 2 to CommunalContentSize.FixedSize.HALF, + 3 to CommunalContentSize.FixedSize.HALF, ) .inOrder() - underTest.resizeWidget(2, CommunalContentSize.FULL.span, emptyMap()) + underTest.resizeWidget(2, CommunalContentSize.FixedSize.FULL.span, emptyMap()) // Widget 2 should have been resized to FULL assertThat(widgetContent?.map { it.appWidgetId to it.size }) .containsExactly( - 1 to CommunalContentSize.HALF, - 2 to CommunalContentSize.FULL, - 3 to CommunalContentSize.HALF, + 1 to CommunalContentSize.FixedSize.HALF, + 2 to CommunalContentSize.FixedSize.FULL, + 3 to CommunalContentSize.FixedSize.HALF, + ) + .inOrder() + } + + @Test + @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID) + fun resizeWidget_withoutUpdatingOrder_responsive() = + testScope.runTest { + val userInfos = listOf(MAIN_USER_INFO) + userRepository.setUserInfos(userInfos) + userTracker.set(userInfos = userInfos, selectedUserIndex = 0) + runCurrent() + + // Widgets available. + widgetRepository.addWidget( + appWidgetId = 1, + userId = MAIN_USER_INFO.id, + rank = 0, + spanY = 1, + ) + widgetRepository.addWidget( + appWidgetId = 2, + userId = MAIN_USER_INFO.id, + rank = 1, + spanY = 1, + ) + widgetRepository.addWidget( + appWidgetId = 3, + userId = MAIN_USER_INFO.id, + rank = 2, + spanY = 1, + ) + + val widgetContent by collectLastValue(underTest.widgetContent) + + assertThat(widgetContent?.map { it.appWidgetId to it.size }) + .containsExactly( + 1 to CommunalContentSize.Responsive(1), + 2 to CommunalContentSize.Responsive(1), + 3 to CommunalContentSize.Responsive(1), + ) + .inOrder() + + underTest.resizeWidget(appWidgetId = 2, spanY = 5, widgetIdToRankMap = emptyMap()) + + // Widget 2 should have been resized to FULL + assertThat(widgetContent?.map { it.appWidgetId to it.size }) + .containsExactly( + 1 to CommunalContentSize.Responsive(1), + 2 to CommunalContentSize.Responsive(5), + 3 to CommunalContentSize.Responsive(1), ) .inOrder() } @Test @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING) + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun resizeWidget_andUpdateOrder() = testScope.runTest { val userInfos = listOf(MAIN_USER_INFO) @@ -1145,39 +1221,98 @@ class CommunalInteractorTest : SysuiTestCase() { appWidgetId = 1, userId = MAIN_USER_INFO.id, rank = 0, - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, + ) + widgetRepository.addWidget( + appWidgetId = 2, + userId = MAIN_USER_INFO.id, + rank = 1, + spanY = CommunalContentSize.FixedSize.HALF.span, + ) + widgetRepository.addWidget( + appWidgetId = 3, + userId = MAIN_USER_INFO.id, + rank = 2, + spanY = CommunalContentSize.FixedSize.HALF.span, + ) + + val widgetContent by collectLastValue(underTest.widgetContent) + + assertThat(widgetContent?.map { it.appWidgetId to it.size }) + .containsExactly( + 1 to CommunalContentSize.FixedSize.HALF, + 2 to CommunalContentSize.FixedSize.HALF, + 3 to CommunalContentSize.FixedSize.HALF, + ) + .inOrder() + + underTest.resizeWidget( + 2, + CommunalContentSize.FixedSize.FULL.span, + mapOf(2 to 0, 1 to 1), + ) + + // Widget 2 should have been resized to FULL and moved to the front of the list + assertThat(widgetContent?.map { it.appWidgetId to it.size }) + .containsExactly( + 2 to CommunalContentSize.FixedSize.FULL, + 1 to CommunalContentSize.FixedSize.HALF, + 3 to CommunalContentSize.FixedSize.HALF, + ) + .inOrder() + } + + @Test + @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID) + fun resizeWidget_andUpdateOrder_responsive() = + testScope.runTest { + val userInfos = listOf(MAIN_USER_INFO) + userRepository.setUserInfos(userInfos) + userTracker.set(userInfos = userInfos, selectedUserIndex = 0) + runCurrent() + + // Widgets available. + widgetRepository.addWidget( + appWidgetId = 1, + userId = MAIN_USER_INFO.id, + rank = 0, + spanY = 1, ) widgetRepository.addWidget( appWidgetId = 2, userId = MAIN_USER_INFO.id, rank = 1, - spanY = CommunalContentSize.HALF.span, + spanY = 1, ) widgetRepository.addWidget( appWidgetId = 3, userId = MAIN_USER_INFO.id, rank = 2, - spanY = CommunalContentSize.HALF.span, + spanY = 1, ) val widgetContent by collectLastValue(underTest.widgetContent) assertThat(widgetContent?.map { it.appWidgetId to it.size }) .containsExactly( - 1 to CommunalContentSize.HALF, - 2 to CommunalContentSize.HALF, - 3 to CommunalContentSize.HALF, + 1 to CommunalContentSize.Responsive(1), + 2 to CommunalContentSize.Responsive(1), + 3 to CommunalContentSize.Responsive(1), ) .inOrder() - underTest.resizeWidget(2, CommunalContentSize.FULL.span, mapOf(2 to 0, 1 to 1)) + underTest.resizeWidget( + appWidgetId = 2, + spanY = 5, + widgetIdToRankMap = mapOf(2 to 0, 1 to 1), + ) // Widget 2 should have been resized to FULL and moved to the front of the list assertThat(widgetContent?.map { it.appWidgetId to it.size }) .containsExactly( - 2 to CommunalContentSize.FULL, - 1 to CommunalContentSize.HALF, - 3 to CommunalContentSize.HALF, + 2 to CommunalContentSize.Responsive(5), + 1 to CommunalContentSize.Responsive(1), + 3 to CommunalContentSize.Responsive(1), ) .inOrder() } @@ -1191,9 +1326,15 @@ class CommunalInteractorTest : SysuiTestCase() { ) } - private companion object { - val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) - val USER_INFO_WORK = + companion object { + @JvmStatic + @Parameters(name = "{0}") + fun getParams(): List<FlagsParameterization> { + return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID) + } + + private val MAIN_USER_INFO = UserInfo(0, "primary", UserInfo.FLAG_MAIN) + private val USER_INFO_WORK = UserInfo( 10, "work", diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt index 3eba8ff4b198..763ea392deb9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt @@ -18,12 +18,14 @@ package com.android.systemui.communal.view.viewmodel import android.content.ComponentName import android.content.pm.UserInfo +import android.platform.test.annotations.DisableFlags import android.platform.test.flag.junit.FlagsParameterization import android.provider.Settings import android.widget.RemoteViews import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.systemui.Flags.FLAG_COMMUNAL_HUB +import com.android.systemui.Flags.FLAG_COMMUNAL_RESPONSIVE_GRID import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.model.CommunalSmartspaceTimer import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository @@ -248,7 +250,9 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { .isInstanceOf(CommunalContentModel.CtaTileInViewMode::class.java) } + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun ongoingContent_umoAndOneTimer_sizedAppropriately() = testScope.runTest { // Widgets available. @@ -280,11 +284,13 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(timer).isInstanceOf(CommunalContentModel.Smartspace::class.java) assertThat(umo).isInstanceOf(CommunalContentModel.Umo::class.java) - assertThat(timer?.size).isEqualTo(CommunalContentSize.HALF) - assertThat(umo?.size).isEqualTo(CommunalContentSize.HALF) + assertThat(timer?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) + assertThat(umo?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) } + /** TODO(b/378171351): Handle ongoing content in responsive grid. */ @Test + @DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID) fun ongoingContent_umoAndTwoTimers_sizedAppropriately() = testScope.runTest { // Widgets available. @@ -324,9 +330,9 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(umo).isInstanceOf(CommunalContentModel.Umo::class.java) // One full-sized timer and a half-sized timer and half-sized UMO. - assertThat(timer1?.size).isEqualTo(CommunalContentSize.HALF) - assertThat(timer2?.size).isEqualTo(CommunalContentSize.HALF) - assertThat(umo?.size).isEqualTo(CommunalContentSize.FULL) + assertThat(timer1?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) + assertThat(timer2?.size).isEqualTo(CommunalContentSize.FixedSize.HALF) + assertThat(umo?.size).isEqualTo(CommunalContentSize.FixedSize.FULL) } @Test @@ -891,7 +897,8 @@ class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { @JvmStatic @Parameters(name = "{0}") fun getParams(): List<FlagsParameterization> { - return FlagsParameterization.allCombinationsOf().andSceneContainer() + return FlagsParameterization.allCombinationsOf(FLAG_COMMUNAL_RESPONSIVE_GRID) + .andSceneContainer() } } } diff --git a/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json new file mode 100644 index 000000000000..c5a83c4d0d8c --- /dev/null +++ b/packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json @@ -0,0 +1,95 @@ +{ + "formatVersion": 1, + "database": { + "version": 5, + "identityHash": "a83f96ef4babe730b3a00e8acb777a25", + "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, `span_y_new` INTEGER NOT NULL DEFAULT 1)", + "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" + }, + { + "fieldPath": "spanYNew", + "columnName": "span_y_new", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "1" + } + ], + "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, 'a83f96ef4babe730b3a00e8acb777a25')" + ] + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt b/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt index c3d2683ce953..41ea7b6c2202 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt @@ -29,9 +29,7 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.runBlocking /** Utilities for communal backup and restore. */ -class CommunalBackupUtils( - private val context: Context, -) { +class CommunalBackupUtils(private val context: Context) { /** * Retrieves a communal hub state protobuf that represents the current state of the communal @@ -50,6 +48,8 @@ class CommunalBackupUtils( widgetId = widget.widgetId componentName = widget.componentName userSerialNumber = widget.userSerialNumber + spanY = widget.spanY + spanYNew = widget.spanYNew } ) } 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 e72088f37fa7..679d0714b1b4 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,9 +26,11 @@ import androidx.room.RoomDatabase import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase import com.android.systemui.communal.shared.model.GlanceableHubMultiUserHelperImpl +import com.android.systemui.communal.shared.model.SpanValue +import com.android.systemui.communal.shared.model.toResponsive import com.android.systemui.res.R -@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 4) +@Database(entities = [CommunalWidgetItem::class, CommunalItemRank::class], version = 5) abstract class CommunalDatabase : RoomDatabase() { abstract fun communalWidgetDao(): CommunalWidgetDao @@ -59,7 +61,12 @@ abstract class CommunalDatabase : RoomDatabase() { context.resources.getString(R.string.config_communalDatabase), ) .also { builder -> - builder.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4) + builder.addMigrations( + MIGRATION_1_2, + MIGRATION_2_3, + MIGRATION_3_4, + MIGRATION_4_5, + ) builder.fallbackToDestructiveMigration(dropAllTables = true) callback?.let { callback -> builder.addCallback(callback) } } @@ -123,5 +130,30 @@ abstract class CommunalDatabase : RoomDatabase() { ) } } + + /** This migration adds a new spanY column for responsive grid sizing. */ + @VisibleForTesting + val MIGRATION_4_5 = + object : Migration(4, 5) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i(TAG, "Migrating from version 4 to 5") + db.execSQL( + "ALTER TABLE communal_widget_table " + + "ADD COLUMN span_y_new INTEGER NOT NULL DEFAULT 1" + ) + db.query("SELECT item_id, span_y FROM communal_widget_table").use { cursor -> + while (cursor.moveToNext()) { + val id = cursor.getInt(cursor.getColumnIndex("item_id")) + val spanYFixed = + SpanValue.Fixed(cursor.getInt(cursor.getColumnIndex("span_y"))) + val spanYResponsive = spanYFixed.toResponsive() + db.execSQL( + "UPDATE communal_widget_table SET span_y_new = " + + "${spanYResponsive.value} WHERE item_id = $id" + ) + } + } + } + } } } 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 f9d2a843c213..6ef4bb8e55eb 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 @@ -45,7 +45,12 @@ data class CommunalWidgetItem( * The vertical span of the widget. Span_Y default value corresponds to * CommunalContentSize.HALF.span */ - @ColumnInfo(name = "span_y", defaultValue = "3") val spanY: Int, + @Deprecated("Use spanYNew instead") + @ColumnInfo(name = "span_y", defaultValue = "3") + val spanY: Int, + + /** The vertical span of the widget in grid cell units. */ + @ColumnInfo(name = "span_y_new", defaultValue = "1") val spanYNew: 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 3d40aa75b488..3907a37cd5d9 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 @@ -27,7 +27,9 @@ import androidx.room.Update import androidx.sqlite.db.SupportSQLiteDatabase import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.communal.nano.CommunalHubState -import com.android.systemui.communal.shared.model.CommunalContentSize +import com.android.systemui.communal.shared.model.SpanValue +import com.android.systemui.communal.shared.model.toFixed +import com.android.systemui.communal.shared.model.toResponsive import com.android.systemui.communal.widgets.CommunalWidgetHost import com.android.systemui.communal.widgets.CommunalWidgetModule.Companion.DEFAULT_WIDGETS import com.android.systemui.dagger.SysUISingleton @@ -101,6 +103,7 @@ constructor( componentName = name, rank = index, userSerialNumber = userSerialNumber, + spanY = SpanValue.Fixed(3), ) } } @@ -155,15 +158,16 @@ interface CommunalWidgetDao { @Query( "INSERT INTO communal_widget_table" + - "(widget_id, component_name, item_id, user_serial_number, span_y) " + - "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY)" + "(widget_id, component_name, item_id, user_serial_number, span_y, span_y_new) " + + "VALUES(:widgetId, :componentName, :itemId, :userSerialNumber, :spanY, :spanYNew)" ) fun insertWidget( widgetId: Int, componentName: String, itemId: Long, userSerialNumber: Int, - spanY: Int = 3, + spanY: Int, + spanYNew: Int, ): Long @Query("INSERT INTO communal_item_rank_table(rank) VALUES(:rank)") @@ -189,10 +193,12 @@ interface CommunalWidgetDao { } @Transaction - fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) { + fun resizeWidget(appWidgetId: Int, spanY: SpanValue, widgetIdToRankMap: Map<Int, Int>) { val widget = getWidgetByIdNow(appWidgetId) if (widget != null) { - updateWidget(widget.copy(spanY = spanY)) + updateWidget( + widget.copy(spanY = spanY.toFixed().value, spanYNew = spanY.toResponsive().value) + ) } updateWidgetOrder(widgetIdToRankMap) } @@ -203,7 +209,7 @@ interface CommunalWidgetDao { provider: ComponentName, rank: Int? = null, userSerialNumber: Int, - spanY: Int = CommunalContentSize.HALF.span, + spanY: SpanValue, ): Long { return addWidget( widgetId = widgetId, @@ -220,7 +226,7 @@ interface CommunalWidgetDao { componentName: String, rank: Int? = null, userSerialNumber: Int, - spanY: Int = 3, + spanY: SpanValue, ): Long { val widgets = getWidgetsNow() @@ -241,7 +247,8 @@ interface CommunalWidgetDao { componentName = componentName, itemId = insertItemRank(newRank), userSerialNumber = userSerialNumber, - spanY = spanY, + spanY = spanY.toFixed().value, + spanYNew = spanY.toResponsive().value, ) } @@ -264,7 +271,11 @@ interface CommunalWidgetDao { clearCommunalItemRankTable() state.widgets.forEach { - val spanY = if (it.spanY != 0) it.spanY else CommunalContentSize.HALF.span + // Check if there is a new value to restore. If so, restore that new value. + val spanYResponsive = if (it.spanYNew != 0) SpanValue.Responsive(it.spanYNew) else null + // If no new value, restore any existing old values. + val spanY = spanYResponsive ?: SpanValue.Fixed(it.spanY.coerceIn(3, 6)) + 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 29569f8b7df5..e44d78baeb35 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 @@ -22,6 +22,7 @@ import android.content.ComponentName import android.os.UserHandle import android.os.UserManager import com.android.app.tracing.coroutines.launchTraced as launch +import com.android.systemui.Flags.communalResponsiveGrid import com.android.systemui.Flags.communalWidgetResizing import com.android.systemui.common.data.repository.PackageChangeRepository import com.android.systemui.common.shared.model.PackageInstallSession @@ -33,6 +34,7 @@ import com.android.systemui.communal.data.db.DefaultWidgetPopulation.SkipReason. import com.android.systemui.communal.nano.CommunalHubState import com.android.systemui.communal.proto.toCommunalHubState import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.SpanValue import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.CommunalWidgetHost import com.android.systemui.communal.widgets.WidgetConfigurator @@ -143,15 +145,21 @@ constructor( componentName = widget.componentName, rank = rank.rank, providerInfo = providers[widget.widgetId], - spanY = widget.spanY, + spanY = if (communalResponsiveGrid()) widget.spanYNew else widget.spanY, ) } } override fun resizeWidget(appWidgetId: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) { if (!communalWidgetResizing()) return + val spanValue = + if (communalResponsiveGrid()) { + SpanValue.Responsive(spanY) + } else { + SpanValue.Fixed(spanY) + } bgScope.launch { - communalWidgetDao.resizeWidget(appWidgetId, spanY, widgetIdToRankMap) + communalWidgetDao.resizeWidget(appWidgetId, spanValue, widgetIdToRankMap) logger.i({ "Updated spanY of widget $int1 to $int2." }) { int1 = appWidgetId int2 = spanY @@ -225,7 +233,7 @@ constructor( provider = provider, rank = rank, userSerialNumber = userManager.getUserSerialNumber(user.identifier), - spanY = 3, + spanY = SpanValue.Fixed(3), ) backupManager.dataChanged() } else { diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 602fe307a1fe..f9b30c6c2ba1 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -34,9 +34,9 @@ import com.android.systemui.communal.data.repository.CommunalWidgetRepository import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent import com.android.systemui.communal.shared.model.CommunalContentSize -import com.android.systemui.communal.shared.model.CommunalContentSize.FULL -import com.android.systemui.communal.shared.model.CommunalContentSize.HALF -import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD +import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL +import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.HALF +import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.THIRD import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel import com.android.systemui.communal.shared.model.EditModeState diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt index 30f580e472e8..da613f58dce8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt @@ -22,6 +22,7 @@ import android.content.ComponentName import android.content.pm.ApplicationInfo import android.graphics.Bitmap import android.widget.RemoteViews +import com.android.systemui.Flags.communalResponsiveGrid import com.android.systemui.communal.shared.model.CommunalContentSize import java.util.UUID @@ -35,7 +36,7 @@ sealed interface CommunalContentModel { /** The minimum size content can be resized to. */ val minSize: CommunalContentSize - get() = CommunalContentSize.HALF + get() = fixedHalfOrResponsiveSize() /** * A type of communal content is ongoing / live / ephemeral, and can be sized and ordered @@ -44,7 +45,12 @@ sealed interface CommunalContentModel { sealed interface Ongoing : CommunalContentModel { override var size: CommunalContentSize override val minSize - get() = CommunalContentSize.THIRD + get() = + if (communalResponsiveGrid()) { + CommunalContentSize.Responsive(1) + } else { + CommunalContentSize.FixedSize.THIRD + } /** Timestamp in milliseconds of when the content was created. */ val createdTimestampMillis: Long @@ -100,14 +106,16 @@ sealed interface CommunalContentModel { class WidgetPlaceholder : CommunalContentModel { override val key: String = KEY.widgetPlaceholder() // Same as widget size. - override val size = CommunalContentSize.HALF + override val size: CommunalContentSize + get() = fixedHalfOrResponsiveSize() } /** A CTA tile in the glanceable hub view mode which can be dismissed. */ class CtaTileInViewMode : CommunalContentModel { override val key: String = KEY.CTA_TILE_IN_VIEW_MODE_KEY // Same as widget size. - override val size = CommunalContentSize.HALF + override val size: CommunalContentSize + get() = fixedHalfOrResponsiveSize() } class Tutorial(id: Int, override var size: CommunalContentSize) : CommunalContentModel { @@ -118,15 +126,15 @@ sealed interface CommunalContentModel { smartspaceTargetId: String, val remoteViews: RemoteViews, override val createdTimestampMillis: Long, - override var size: CommunalContentSize = CommunalContentSize.HALF, + override var size: CommunalContentSize = fixedHalfOrResponsiveSize(), ) : Ongoing { override val key = KEY.smartspace(smartspaceTargetId) } class Umo( override val createdTimestampMillis: Long, - override var size: CommunalContentSize = CommunalContentSize.HALF, - override var minSize: CommunalContentSize = CommunalContentSize.HALF, + override var size: CommunalContentSize = fixedHalfOrResponsiveSize(), + override var minSize: CommunalContentSize = fixedHalfOrResponsiveSize(), ) : Ongoing { override val key = KEY.umo() } @@ -170,3 +178,10 @@ sealed interface CommunalContentModel { fun isLiveContent() = this is Smartspace || this is Umo } + +private fun fixedHalfOrResponsiveSize() = + if (communalResponsiveGrid()) { + CommunalContentSize.Responsive(1) + } else { + CommunalContentSize.FixedSize.HALF + } 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 7602a7afce4e..04717d0c864b 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 @@ -39,7 +39,10 @@ message CommunalHubState { // Serial number of the user associated with the widget. int32 user_serial_number = 4; - // The vertical span of the widget + // The vertical span of the widget, replaced by span_y_new. int32 span_y = 5; + + // The vertical span of the widget. + int32 span_y_new = 6; } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt index cf80b7d62fd5..df30716ebaf2 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt @@ -16,27 +16,39 @@ package com.android.systemui.communal.shared.model +import com.android.systemui.Flags.communalResponsiveGrid + /** * Supported sizes for communal content in the layout grid. * - * @param span The span of the content in a column. For example, if FULL is 6, then 3 represents - * HALF, 2 represents THIRD, and 1 represents SIXTH. + * @property span The span of the content in a column. */ -enum class CommunalContentSize(val span: Int) { - /** Content takes the full height of the column. */ - FULL(6), +sealed interface CommunalContentSize { + val span: Int + + @Deprecated("Use Responsive size instead") + enum class FixedSize(override val span: Int) : CommunalContentSize { + /** Content takes the full height of the column. */ + FULL(6), - /** Content takes half of the height of the column. */ - HALF(3), + /** Content takes half of the height of the column. */ + HALF(3), + + /** Content takes a third of the height of the column. */ + THIRD(2), + } - /** Content takes a third of the height of the column. */ - THIRD(2); + @JvmInline value class Responsive(override val span: Int) : CommunalContentSize companion object { /** Converts from span to communal content size. */ fun toSize(span: Int): CommunalContentSize { - return entries.find { it.span == span } - ?: throw IllegalArgumentException("$span is not a valid span size") + return if (communalResponsiveGrid()) { + Responsive(span) + } else { + FixedSize.entries.find { it.span == span } + ?: throw IllegalArgumentException("$span is not a valid span size") + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt new file mode 100644 index 000000000000..15cc6b0ad2c8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.shared.model + +/** Models possible span values for different grid formats. */ +sealed interface SpanValue { + val value: Int + + @Deprecated("Use Responsive sizes instead") + @JvmInline + value class Fixed(override val value: Int) : SpanValue + + @JvmInline value class Responsive(override val value: Int) : SpanValue +} + +fun SpanValue.toResponsive(): SpanValue.Responsive = + when (this) { + is SpanValue.Responsive -> this + is SpanValue.Fixed -> SpanValue.Responsive((this.value / 3).coerceAtMost(1)) + } + +fun SpanValue.toFixed(): SpanValue.Fixed = + when (this) { + is SpanValue.Fixed -> this + is SpanValue.Responsive -> SpanValue.Fixed((this.value * 3).coerceIn(3, 6)) + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt index 4ca84c58f6d9..50fad3bf697e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt @@ -32,6 +32,7 @@ import com.android.systemui.communal.data.backup.CommunalBackupUtilsTest.FakeWid import com.android.systemui.communal.data.db.CommunalDatabase import com.android.systemui.communal.data.db.CommunalWidgetDao import com.android.systemui.communal.proto.toCommunalHubState +import com.android.systemui.communal.shared.model.SpanValue import com.android.systemui.lifecycle.InstantTaskExecutorRule import com.google.common.truth.Truth.assertThat import java.io.File @@ -120,21 +121,32 @@ class CommunalBackupHelperTest : SysuiTestCase() { componentName = "com.android.fakePackage1/fakeWidget1", rank = 0, userSerialNumber = 0, + spanY = SpanValue.Responsive(1), ), FakeWidgetMetadata( widgetId = 12, componentName = "com.android.fakePackage2/fakeWidget2", rank = 1, userSerialNumber = 0, + spanY = SpanValue.Responsive(2), ), FakeWidgetMetadata( widgetId = 13, componentName = "com.android.fakePackage3/fakeWidget3", rank = 2, userSerialNumber = 10, + spanY = SpanValue.Responsive(3), ), ) - .onEach { dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber) } + .onEach { + dao.addWidget( + widgetId = it.widgetId, + componentName = it.componentName, + rank = it.rank, + userSerialNumber = it.userSerialNumber, + spanY = it.spanY, + ) + } } private fun getBackupDataInputStream(): BackupDataInputStream { diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt index edc8c837bf78..d31e4664d4a2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt @@ -23,6 +23,9 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.communal.data.db.CommunalDatabase import com.android.systemui.communal.data.db.CommunalWidgetDao import com.android.systemui.communal.nano.CommunalHubState +import com.android.systemui.communal.shared.model.SpanValue +import com.android.systemui.communal.shared.model.toFixed +import com.android.systemui.communal.shared.model.toResponsive import com.android.systemui.lifecycle.InstantTaskExecutorRule import com.google.common.truth.Correspondence import com.google.common.truth.Truth.assertThat @@ -71,22 +74,25 @@ class CommunalBackupUtilsTest : SysuiTestCase() { componentName = "com.android.fakePackage1/fakeWidget1", rank = 0, userSerialNumber = 0, + spanY = SpanValue.Responsive(1), ), FakeWidgetMetadata( widgetId = 12, componentName = "com.android.fakePackage2/fakeWidget2", rank = 1, userSerialNumber = 0, + spanY = SpanValue.Responsive(2), ), FakeWidgetMetadata( widgetId = 13, componentName = "com.android.fakePackage3/fakeWidget3", rank = 2, userSerialNumber = 10, + spanY = SpanValue.Responsive(3), ), ) expectedWidgets.forEach { - dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber) + dao.addWidget(it.widgetId, it.componentName, it.rank, it.userSerialNumber, it.spanY) } // Get communal hub state @@ -150,6 +156,7 @@ class CommunalBackupUtilsTest : SysuiTestCase() { val componentName: String, val rank: Int, val userSerialNumber: Int, + val spanY: SpanValue, ) companion object { @@ -163,7 +170,9 @@ class CommunalBackupUtilsTest : SysuiTestCase() { actual?.widgetId == expected?.widgetId && actual?.componentName == expected?.componentName && actual?.rank == expected?.rank && - actual?.userSerialNumber == expected?.userSerialNumber + actual?.userSerialNumber == expected?.userSerialNumber && + actual?.spanY == expected?.spanY?.toFixed()?.value && + actual?.spanYNew == expected?.spanY?.toResponsive()?.value }, "represents", ) 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 7d5a334b45ea..1466e32b1296 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 @@ -22,6 +22,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import androidx.test.platform.app.InstrumentationRegistry import com.android.systemui.SysuiTestCase +import com.android.systemui.communal.shared.model.SpanValue +import com.android.systemui.communal.shared.model.toResponsive import com.android.systemui.lifecycle.InstantTaskExecutorRule import com.google.common.truth.Truth.assertThat import org.junit.Rule @@ -173,6 +175,49 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { databaseV4.verifyWidgetsV4(fakeWidgetsV3.map { it.getV4() }) } + @Test + fun migrate4To5_addNewSpanYColumn() { + val databaseV4 = migrationTestHelper.createDatabase(DATABASE_NAME, version = 4) + + val fakeWidgetsV4 = + listOf( + FakeCommunalWidgetItemV4( + widgetId = 1, + componentName = "test_widget_1", + itemId = 11, + userSerialNumber = 0, + spanY = 3, + ), + FakeCommunalWidgetItemV4( + widgetId = 2, + componentName = "test_widget_2", + itemId = 12, + userSerialNumber = 10, + spanY = 6, + ), + FakeCommunalWidgetItemV4( + widgetId = 3, + componentName = "test_widget_3", + itemId = 13, + userSerialNumber = 0, + spanY = 0, + ), + ) + databaseV4.insertWidgetsV4(fakeWidgetsV4) + + databaseV4.verifyWidgetsV4(fakeWidgetsV4) + + val databaseV5 = + migrationTestHelper.runMigrationsAndValidate( + name = DATABASE_NAME, + version = 5, + validateDroppedTables = false, + CommunalDatabase.MIGRATION_4_5, + ) + + databaseV5.verifyWidgetsV5(fakeWidgetsV4.map { it.getV5() }) + } + private fun SupportSQLiteDatabase.insertWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) { widgets.forEach { widget -> execSQL( @@ -198,6 +243,24 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { } } + private fun SupportSQLiteDatabase.insertWidgetsV4(widgets: List<FakeCommunalWidgetItemV4>) { + widgets.forEach { widget -> + execSQL( + "INSERT INTO communal_widget_table(" + + "widget_id, " + + "component_name, " + + "item_id, " + + "user_serial_number, " + + "span_y) " + + "VALUES(${widget.widgetId}, " + + "'${widget.componentName}', " + + "${widget.itemId}, " + + "${widget.userSerialNumber}," + + "${widget.spanY})" + ) + } + } + private fun SupportSQLiteDatabase.verifyWidgetsV1(widgets: List<FakeCommunalWidgetItemV1>) { val cursor = query("SELECT * FROM communal_widget_table") assertThat(cursor.moveToFirst()).isTrue() @@ -270,6 +333,27 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { assertThat(cursor.isAfterLast).isTrue() } + private fun SupportSQLiteDatabase.verifyWidgetsV5(widgets: List<FakeCommunalWidgetItemV5>) { + 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) + assertThat(cursor.getInt(cursor.getColumnIndex("span_y_new"))) + .isEqualTo(widget.spanYNew) + + 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})") @@ -334,6 +418,27 @@ class CommunalDatabaseMigrationsTest : SysuiTestCase() { val spanY: Int, ) + private fun FakeCommunalWidgetItemV4.getV5(): FakeCommunalWidgetItemV5 { + val spanYFixed = SpanValue.Fixed(spanY) + return FakeCommunalWidgetItemV5( + widgetId = widgetId, + componentName = componentName, + itemId = itemId, + userSerialNumber = userSerialNumber, + spanY = spanYFixed.value, + spanYNew = spanYFixed.toResponsive().value, + ) + } + + private data class FakeCommunalWidgetItemV5( + val widgetId: Int, + val componentName: String, + val itemId: Int, + val userSerialNumber: Int, + val spanY: Int, + val spanYNew: Int, + ) + private data class FakeCommunalItemRank(val rank: Int) companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt index 2312bbd2d7f8..2acb7750273b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt @@ -22,7 +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.communal.shared.model.SpanValue import com.android.systemui.coroutines.collectLastValue import com.android.systemui.lifecycle.InstantTaskExecutorRule import com.google.common.truth.Truth.assertThat @@ -68,12 +68,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { @Test fun addWidget_readValueInDb() = testScope.runTest { - val (widgetId, provider, rank, userSerialNumber) = widgetInfo1 + val (widgetId, provider, rank, userSerialNumber, spanY) = widgetInfo1 communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) val entry = communalWidgetDao.getWidgetByIdNow(id = 1) assertThat(entry).isEqualTo(communalWidgetItemEntry1) @@ -82,12 +83,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { @Test fun deleteWidget_notInDb_returnsFalse() = testScope.runTest { - val (widgetId, provider, rank, userSerialNumber) = widgetInfo1 + val (widgetId, provider, rank, userSerialNumber, spanY) = widgetInfo1 communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) assertThat(communalWidgetDao.deleteWidgetById(widgetId = 123)).isFalse() } @@ -98,12 +100,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { val widgetsToAdd = listOf(widgetInfo1, widgetInfo2) val widgets = collectLastValue(communalWidgetDao.getWidgets()) widgetsToAdd.forEach { - val (widgetId, provider, rank, userSerialNumber) = it + val (widgetId, provider, rank, userSerialNumber, spanY) = it communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) } assertThat(widgets()) @@ -126,11 +129,12 @@ class CommunalWidgetDaoTest : SysuiTestCase() { // Add widgets one by one without specifying rank val widgetsToAdd = listOf(widgetInfo1, widgetInfo2, widgetInfo3) widgetsToAdd.forEach { - val (widgetId, provider, _, userSerialNumber) = it + val (widgetId, provider, _, userSerialNumber, spanY) = it communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, userSerialNumber = userSerialNumber, + spanY = spanY, ) } @@ -153,12 +157,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { val widgets = collectLastValue(communalWidgetDao.getWidgets()) widgetsToAdd.forEach { - val (widgetId, provider, rank, userSerialNumber) = it + val (widgetId, provider, rank, userSerialNumber, spanY) = it communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) } assertThat(widgets()) @@ -180,12 +185,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { val widgets = collectLastValue(communalWidgetDao.getWidgets()) widgetsToAdd.forEach { - val (widgetId, provider, rank, userSerialNumber) = it + val (widgetId, provider, rank, userSerialNumber, spanY) = it communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) } assertThat(widgets()) @@ -217,12 +223,13 @@ class CommunalWidgetDaoTest : SysuiTestCase() { val widgets = collectLastValue(communalWidgetDao.getWidgets()) existingWidgets.forEach { - val (widgetId, provider, rank, userSerialNumber) = it + val (widgetId, provider, rank, userSerialNumber, spanY) = it communalWidgetDao.addWidget( widgetId = widgetId, provider = provider, rank = rank, userSerialNumber = userSerialNumber, + spanY = spanY, ) } assertThat(widgets()) @@ -242,6 +249,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = ComponentName("pk_name", "cls_name_4"), rank = 1, userSerialNumber = 0, + spanY = SpanValue.Responsive(1), ) val newRankEntry = CommunalItemRank(uid = 4L, rank = 1) @@ -253,6 +261,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { itemId = 4L, userSerialNumber = 0, spanY = 3, + spanYNew = 1, ) assertThat(widgets()) .containsExactly( @@ -279,21 +288,21 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = ComponentName("pkg_name", "cls_name_1"), rank = 0, userSerialNumber = 0, - spanY = CommunalContentSize.FULL.span, + spanY = SpanValue.Responsive(1), ) communalWidgetDao.addWidget( widgetId = 2, provider = ComponentName("pkg_name", "cls_name_2"), rank = 1, userSerialNumber = 0, - spanY = CommunalContentSize.HALF.span, + spanY = SpanValue.Responsive(2), ) communalWidgetDao.addWidget( widgetId = 3, provider = ComponentName("pkg_name", "cls_name_3"), rank = 2, userSerialNumber = 0, - spanY = CommunalContentSize.THIRD.span, + spanY = SpanValue.Fixed(3), ) // Verify that the widgets have the correct spanY values @@ -306,7 +315,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = "pkg_name/cls_name_1", itemId = 1L, userSerialNumber = 0, - spanY = CommunalContentSize.FULL.span, + spanY = 3, + spanYNew = 1, ), CommunalItemRank(uid = 2L, rank = 1), CommunalWidgetItem( @@ -315,7 +325,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = "pkg_name/cls_name_2", itemId = 2L, userSerialNumber = 0, - spanY = CommunalContentSize.HALF.span, + spanY = 6, + spanYNew = 2, ), CommunalItemRank(uid = 3L, rank = 2), CommunalWidgetItem( @@ -324,7 +335,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = "pkg_name/cls_name_3", itemId = 3L, userSerialNumber = 0, - spanY = CommunalContentSize.THIRD.span, + spanY = 3, + spanYNew = 1, ), ) .inOrder() @@ -352,7 +364,8 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = fakeWidget.componentName, itemId = rank.uid, userSerialNumber = fakeWidget.userSerialNumber, - spanY = 3, + spanY = fakeWidget.spanY.coerceAtLeast(3), + spanYNew = fakeWidget.spanYNew.coerceAtLeast(1), ) expected[rank] = widget } @@ -366,6 +379,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = metadata.provider, rank = rank ?: metadata.rank, userSerialNumber = metadata.userSerialNumber, + spanY = metadata.spanY, ) } @@ -374,6 +388,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { val provider: ComponentName, val rank: Int, val userSerialNumber: Int, + val spanY: SpanValue, ) companion object { @@ -383,6 +398,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = ComponentName("pk_name", "cls_name_1"), rank = 0, userSerialNumber = 0, + spanY = SpanValue.Responsive(1), ) val widgetInfo2 = FakeWidgetMetadata( @@ -390,6 +406,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = ComponentName("pk_name", "cls_name_2"), rank = 1, userSerialNumber = 0, + spanY = SpanValue.Responsive(1), ) val widgetInfo3 = FakeWidgetMetadata( @@ -397,6 +414,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { provider = ComponentName("pk_name", "cls_name_3"), rank = 2, userSerialNumber = 10, + spanY = SpanValue.Responsive(1), ) val communalItemRankEntry1 = CommunalItemRank(uid = 1L, rank = widgetInfo1.rank) val communalItemRankEntry2 = CommunalItemRank(uid = 2L, rank = widgetInfo2.rank) @@ -409,6 +427,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { itemId = communalItemRankEntry1.uid, userSerialNumber = widgetInfo1.userSerialNumber, spanY = 3, + spanYNew = 1, ) val communalWidgetItemEntry2 = CommunalWidgetItem( @@ -418,6 +437,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { itemId = communalItemRankEntry2.uid, userSerialNumber = widgetInfo2.userSerialNumber, spanY = 3, + spanYNew = 1, ) val communalWidgetItemEntry3 = CommunalWidgetItem( @@ -427,6 +447,7 @@ class CommunalWidgetDaoTest : SysuiTestCase() { itemId = communalItemRankEntry3.uid, userSerialNumber = widgetInfo3.userSerialNumber, spanY = 3, + spanYNew = 1, ) val fakeState = CommunalHubState().apply { @@ -437,12 +458,14 @@ class CommunalWidgetDaoTest : SysuiTestCase() { componentName = "pk_name/fake_widget_1" rank = 1 userSerialNumber = 0 + spanY = 3 }, CommunalHubState.CommunalWidgetItem().apply { widgetId = 2 componentName = "pk_name/fake_widget_2" rank = 2 userSerialNumber = 10 + spanYNew = 1 }, ) .toTypedArray() 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 3b175853de7d..1f7f3bc4be2b 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 @@ -55,7 +55,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : rank: Int = 0, category: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD, userId: Int = 0, - spanY: Int = CommunalContentSize.HALF.span, + spanY: Int = CommunalContentSize.FixedSize.HALF.span, ) { fakeDatabase[appWidgetId] = CommunalWidgetContentModel.Available( @@ -87,7 +87,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : componentName = ComponentName.unflattenFromString(componentName)!!, icon = icon, user = UserHandle(userId), - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, ) updateListFromDatabase() } @@ -143,7 +143,7 @@ class FakeCommunalWidgetRepository(private val coroutineScope: CoroutineScope) : appWidgetId = id, providerInfo = providerInfo, rank = rank, - spanY = CommunalContentSize.HALF.span, + spanY = CommunalContentSize.FixedSize.HALF.span, ) updateListFromDatabase() } |