summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-12-02 17:45:32 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2024-12-02 17:45:32 +0000
commit0ae597a9d8d8abc9517f0d39ed1a9a1ed42dc66e (patch)
tree0cea4c80fa259bc23694028adb2cdeae57f0acc7
parentd9f7109671e30cec35de60569cc145e7cb23169d (diff)
parentcefca220fb1f57ace39abe133899f5b9115421f1 (diff)
Merge changes Id312bbfb,I147e1ba5 into main
* changes: Update communal hub to use responsive grid Update communal db for responsive grid
-rw-r--r--packages/SystemUI/aconfig/systemui.aconfig7
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt119
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/ResponsiveLazyHorizontalGrid.kt14
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/db/DefaultWidgetPopulationTest.kt9
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryLocalImplTest.kt327
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt241
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt19
-rw-r--r--packages/SystemUI/schemas/com.android.systemui.communal.data.db.CommunalDatabase/5.json95
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/backup/CommunalBackupUtils.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalDatabase.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalEntities.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/db/CommunalWidgetDao.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepository.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/model/CommunalContentModel.kt29
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/proto/communal_hub_state.proto5
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalContentSize.kt34
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/shared/model/SpanValue.kt40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupHelperTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/backup/CommunalBackupUtilsTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalDatabaseMigrationsTest.kt105
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/communal/data/db/CommunalWidgetDaoTest.kt53
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalWidgetRepository.kt6
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()
}