Merge "Implement grid consistency interactor." into main
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
index e3ba36f..0696fbe 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/dagger/PanelsModule.kt
@@ -16,8 +16,17 @@
package com.android.systemui.qs.panels.dagger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
+import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepositoryImpl
import com.android.systemui.qs.panels.data.repository.IconTilesRepository
import com.android.systemui.qs.panels.data.repository.IconTilesRepositoryImpl
+import com.android.systemui.qs.panels.domain.interactor.GridTypeConsistencyInteractor
+import com.android.systemui.qs.panels.domain.interactor.InfiniteGridConsistencyInteractor
+import com.android.systemui.qs.panels.domain.interactor.NoopGridConsistencyInteractor
+import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import com.android.systemui.qs.panels.ui.compose.GridLayout
@@ -31,8 +40,23 @@
interface PanelsModule {
@Binds fun bindIconTilesRepository(impl: IconTilesRepositoryImpl): IconTilesRepository
+ @Binds
+ fun bindGridLayoutTypeRepository(impl: GridLayoutTypeRepositoryImpl): GridLayoutTypeRepository
+
+ @Binds
+ fun bindDefaultGridConsistencyInteractor(
+ impl: NoopGridConsistencyInteractor
+ ): GridTypeConsistencyInteractor
+
companion object {
@Provides
+ @SysUISingleton
+ @GridConsistencyLog
+ fun providesGridConsistencyLog(factory: LogBufferFactory): LogBuffer {
+ return factory.create("GridConsistencyLog", 50)
+ }
+
+ @Provides
@IntoSet
fun provideGridLayout(gridLayout: InfiniteGridLayout): Pair<GridLayoutType, GridLayout> {
return Pair(InfiniteGridLayoutType, gridLayout)
@@ -44,5 +68,20 @@
): Map<GridLayoutType, GridLayout> {
return entries.toMap()
}
+
+ @Provides
+ @IntoSet
+ fun provideGridConsistencyInteractor(
+ consistencyInteractor: InfiniteGridConsistencyInteractor
+ ): Pair<GridLayoutType, GridTypeConsistencyInteractor> {
+ return Pair(InfiniteGridLayoutType, consistencyInteractor)
+ }
+
+ @Provides
+ fun provideGridConsistencyInteractorMap(
+ entries: Set<@JvmSuppressWildcards Pair<GridLayoutType, GridTypeConsistencyInteractor>>
+ ): Map<GridLayoutType, GridTypeConsistencyInteractor> {
+ return entries.toMap()
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
index 02dd33e..542d0cb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepository.kt
@@ -20,10 +20,16 @@
import com.android.systemui.qs.panels.shared.model.GridLayoutType
import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+interface GridLayoutTypeRepository {
+ val layout: StateFlow<GridLayoutType>
+}
@SysUISingleton
-class GridLayoutTypeRepository @Inject constructor() {
- val layout: Flow<GridLayoutType> = flowOf(InfiniteGridLayoutType)
+class GridLayoutTypeRepositoryImpl @Inject constructor() : GridLayoutTypeRepository {
+ private val _layout: MutableStateFlow<GridLayoutType> = MutableStateFlow(InfiniteGridLayoutType)
+ override val layout = _layout.asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
index 92f87e7..e581bfc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/IconTilesRepository.kt
@@ -19,20 +19,20 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
/** Repository for retrieving the list of [TileSpec] to be displayed as icons. */
interface IconTilesRepository {
- val iconTilesSpecs: Flow<Set<TileSpec>>
+ val iconTilesSpecs: StateFlow<Set<TileSpec>>
}
@SysUISingleton
class IconTilesRepositoryImpl @Inject constructor() : IconTilesRepository {
- /** Set of toggleable tiles that are suitable for being shown as an icon. */
- override val iconTilesSpecs: Flow<Set<TileSpec>> =
- flowOf(
+ private val _iconTilesSpecs =
+ MutableStateFlow(
setOf(
TileSpec.create("airplane"),
TileSpec.create("battery"),
@@ -50,4 +50,7 @@
TileSpec.create("rotation")
)
)
+
+ /** Set of toggleable tiles that are suitable for being shown as an icon. */
+ override val iconTilesSpecs: StateFlow<Set<TileSpec>> = _iconTilesSpecs.asStateFlow()
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt
new file mode 100644
index 0000000..43ccdf66
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepository.kt
@@ -0,0 +1,30 @@
+/*
+ * 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.qs.panels.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+@SysUISingleton
+class InfiniteGridSizeRepository @Inject constructor() {
+ // Number of columns in the narrowest state for consistency
+ private val _columns = MutableStateFlow(4)
+ val columns: StateFlow<Int> = _columns.asStateFlow()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractor.kt
new file mode 100644
index 0000000..7732092
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractor.kt
@@ -0,0 +1,75 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.qs.panels.shared.model.GridConsistencyLog
+import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class GridConsistencyInteractor
+@Inject
+constructor(
+ private val gridLayoutTypeInteractor: GridLayoutTypeInteractor,
+ private val currentTilesInteractor: CurrentTilesInteractor,
+ private val consistencyInteractors:
+ Map<GridLayoutType, @JvmSuppressWildcards GridTypeConsistencyInteractor>,
+ private val defaultConsistencyInteractor: GridTypeConsistencyInteractor,
+ @GridConsistencyLog private val logBuffer: LogBuffer,
+ @Application private val applicationScope: CoroutineScope,
+) {
+ fun start() {
+ applicationScope.launch {
+ gridLayoutTypeInteractor.layout.collectLatest { type ->
+ val consistencyInteractor =
+ consistencyInteractors[type] ?: defaultConsistencyInteractor
+ currentTilesInteractor.currentTiles
+ .map { tiles -> tiles.map { it.spec } }
+ .collectLatest { tiles ->
+ val newTiles = consistencyInteractor.reconcileTiles(tiles)
+ if (newTiles != tiles) {
+ currentTilesInteractor.setTiles(newTiles)
+ logChange(newTiles)
+ }
+ }
+ }
+ }
+ }
+
+ private fun logChange(tiles: List<TileSpec>) {
+ logBuffer.log(
+ LOG_BUFFER_CURRENT_TILES_CHANGE_TAG,
+ LogLevel.DEBUG,
+ { str1 = tiles.toString() },
+ { "Tiles reordered: $str1" }
+ )
+ }
+
+ private companion object {
+ const val LOG_BUFFER_CURRENT_TILES_CHANGE_TAG = "GridConsistencyTilesChange"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridTypeConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridTypeConsistencyInteractor.kt
new file mode 100644
index 0000000..4cdabae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/GridTypeConsistencyInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.qs.pipeline.shared.TileSpec
+
+interface GridTypeConsistencyInteractor {
+ /**
+ * Given a list of tiles, return the best list of the same tiles (preserving as much order as
+ * possible, such that it's consistent with the current layout.
+ */
+ fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
index 1aec193..ccc1c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/IconTilesInteractor.kt
@@ -20,10 +20,10 @@
import com.android.systemui.qs.panels.data.repository.IconTilesRepository
import com.android.systemui.qs.pipeline.shared.TileSpec
import javax.inject.Inject
-import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.StateFlow
/** Interactor for retrieving the list of [TileSpec] to be displayed as icons. */
@SysUISingleton
class IconTilesInteractor @Inject constructor(repo: IconTilesRepository) {
- val iconTilesSpecs: Flow<Set<TileSpec>> = repo.iconTilesSpecs
+ val iconTilesSpecs: StateFlow<Set<TileSpec>> = repo.iconTilesSpecs
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
new file mode 100644
index 0000000..74e906c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractor.kt
@@ -0,0 +1,143 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+@SysUISingleton
+class InfiniteGridConsistencyInteractor
+@Inject
+constructor(
+ private val iconTilesInteractor: IconTilesInteractor,
+ private val gridSizeInteractor: InfiniteGridSizeInteractor
+) : GridTypeConsistencyInteractor {
+
+ /**
+ * Tries to fill in every columns of all rows (except the last row), potentially reordering
+ * tiles.
+ */
+ override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> {
+ val newTiles: MutableList<TileSpec> = mutableListOf()
+ val row = TileRow(columns = gridSizeInteractor.columns.value)
+ val iconTilesSet = iconTilesInteractor.iconTilesSpecs.value
+ val tilesQueue =
+ ArrayDeque(
+ tiles.map {
+ SizedTile(
+ it,
+ width =
+ if (iconTilesSet.contains(it)) {
+ 1
+ } else {
+ 2
+ }
+ )
+ }
+ )
+
+ while (tilesQueue.isNotEmpty()) {
+ if (row.isFull()) {
+ newTiles.addAll(row.tileSpecs())
+ row.clear()
+ }
+
+ val tile = tilesQueue.removeFirst()
+
+ // If the tile fits in the row, add it.
+ if (!row.maybeAddTile(tile)) {
+ // If the tile does not fit the row, find an icon tile to move.
+ // We'll try to either add an icon tile from the queue to complete the row, or
+ // remove an icon tile from the current row to free up space.
+
+ val iconTile: SizedTile? = tilesQueue.firstOrNull { it.width == 1 }
+ if (iconTile != null) {
+ tilesQueue.remove(iconTile)
+ tilesQueue.addFirst(tile)
+ row.maybeAddTile(iconTile)
+ } else {
+ val tileToRemove: SizedTile? = row.findLastIconTile()
+ if (tileToRemove != null) {
+ row.removeTile(tileToRemove)
+ row.maybeAddTile(tile)
+
+ // Moving the icon tile to the end because there's no other
+ // icon tiles in the queue.
+ tilesQueue.addLast(tileToRemove)
+ } else {
+ // If the row does not have an icon tile, add the incomplete row.
+ // Note: this shouldn't happen because an icon tile is guaranteed to be in a
+ // row that doesn't have enough space for a large tile.
+ val tileSpecs = row.tileSpecs()
+ Log.wtf(TAG, "Uneven row does not have an icon tile to remove: $tileSpecs")
+ newTiles.addAll(tileSpecs)
+ row.clear()
+ tilesQueue.addFirst(tile)
+ }
+ }
+ }
+ }
+
+ // Add last row that might be incomplete
+ newTiles.addAll(row.tileSpecs())
+
+ return newTiles.toList()
+ }
+
+ /** Tile with a width representing the number of columns it should take. */
+ private data class SizedTile(val spec: TileSpec, val width: Int)
+
+ private class TileRow(private val columns: Int) {
+ private var availableColumns = columns
+ private val tiles: MutableList<SizedTile> = mutableListOf()
+
+ fun tileSpecs(): List<TileSpec> {
+ return tiles.map { it.spec }
+ }
+
+ fun maybeAddTile(tile: SizedTile): Boolean {
+ if (availableColumns - tile.width >= 0) {
+ tiles.add(tile)
+ availableColumns -= tile.width
+ return true
+ }
+ return false
+ }
+
+ fun findLastIconTile(): SizedTile? {
+ return tiles.findLast { it.width == 1 }
+ }
+
+ fun removeTile(tile: SizedTile) {
+ tiles.remove(tile)
+ availableColumns += tile.width
+ }
+
+ fun clear() {
+ tiles.clear()
+ availableColumns = columns
+ }
+
+ fun isFull(): Boolean = availableColumns == 0
+ }
+
+ private companion object {
+ const val TAG = "InfiniteGridConsistencyInteractor"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt
new file mode 100644
index 0000000..13c6072
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.panels.data.repository.InfiniteGridSizeRepository
+import javax.inject.Inject
+import kotlinx.coroutines.flow.StateFlow
+
+@SysUISingleton
+class InfiniteGridSizeInteractor @Inject constructor(repo: InfiniteGridSizeRepository) {
+ val columns: StateFlow<Int> = repo.columns
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractor.kt
new file mode 100644
index 0000000..0386a6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractor.kt
@@ -0,0 +1,27 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+
+/** [GridTypeConsistencyInteractor] implementation that doesn't do any changes to tiles. */
+@SysUISingleton
+class NoopGridConsistencyInteractor @Inject constructor() : GridTypeConsistencyInteractor {
+ override fun reconcileTiles(tiles: List<TileSpec>): List<TileSpec> = tiles
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridConsistencyLog.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridConsistencyLog.kt
new file mode 100644
index 0000000..884cde3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/shared/model/GridConsistencyLog.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.panels.shared.model
+
+import javax.inject.Qualifier
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class GridConsistencyLog()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
index 86cc6f5..dc43091 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt
@@ -66,7 +66,6 @@
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.res.integerResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
@@ -80,6 +79,7 @@
import com.android.systemui.common.ui.compose.load
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.qs.panels.domain.interactor.IconTilesInteractor
+import com.android.systemui.qs.panels.domain.interactor.InfiniteGridSizeInteractor
import com.android.systemui.qs.panels.ui.viewmodel.ActiveTileColorAttributes
import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
@@ -97,8 +97,12 @@
import kotlinx.coroutines.flow.mapLatest
@SysUISingleton
-class InfiniteGridLayout @Inject constructor(private val iconTilesInteractor: IconTilesInteractor) :
- GridLayout {
+class InfiniteGridLayout
+@Inject
+constructor(
+ private val iconTilesInteractor: IconTilesInteractor,
+ private val gridSizeInteractor: InfiniteGridSizeInteractor
+) : GridLayout {
private object TileType
@@ -112,10 +116,10 @@
tiles.forEach { it.startListening(token) }
onDispose { tiles.forEach { it.stopListening(token) } }
}
- val iconTilesSpecs by
- iconTilesInteractor.iconTilesSpecs.collectAsState(initial = emptySet())
+ val iconTilesSpecs by iconTilesInteractor.iconTilesSpecs.collectAsState()
+ val columns by gridSizeInteractor.columns.collectAsState()
- TileLazyGrid(modifier) {
+ TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
items(
tiles.size,
span = { index ->
@@ -200,8 +204,9 @@
val iconOnlySpecs by iconTilesInteractor.iconTilesSpecs.collectAsState(initial = emptySet())
val isIconOnly: (TileSpec) -> Boolean =
remember(iconOnlySpecs) { { tileSpec: TileSpec -> tileSpec in iconOnlySpecs } }
+ val columns by gridSizeInteractor.columns.collectAsState()
- TileLazyGrid(modifier = modifier) {
+ TileLazyGrid(modifier = modifier, columns = GridCells.Fixed(columns)) {
// These Text are just placeholders to see the different sections. Not final UI.
item(span = { GridItemSpan(maxLineSpan) }) {
Text("Current tiles", color = Color.White)
@@ -385,11 +390,11 @@
@Composable
private fun TileLazyGrid(
modifier: Modifier = Modifier,
+ columns: GridCells,
content: LazyGridScope.() -> Unit,
) {
LazyVerticalGrid(
- columns =
- GridCells.Fixed(integerResource(R.integer.quick_settings_infinite_grid_num_columns)),
+ columns = columns,
verticalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_vertical)),
horizontalArrangement = spacedBy(dimensionResource(R.dimen.qs_tile_margin_horizontal)),
modifier = modifier,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
index af1d195..c8fbeb5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/domain/startable/QSPipelineCoreStartable.kt
@@ -18,6 +18,8 @@
import com.android.systemui.CoreStartable
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.flags.NewQsUI
+import com.android.systemui.qs.panels.domain.interactor.GridConsistencyInteractor
import com.android.systemui.qs.pipeline.domain.interactor.AccessibilityTilesInteractor
import com.android.systemui.qs.pipeline.domain.interactor.AutoAddInteractor
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
@@ -34,6 +36,7 @@
private val autoAddInteractor: AutoAddInteractor,
private val featureFlags: QSPipelineFlagsRepository,
private val restoreReconciliationInteractor: RestoreReconciliationInteractor,
+ private val gridConsistencyInteractor: GridConsistencyInteractor,
) : CoreStartable {
override fun start() {
@@ -41,6 +44,10 @@
accessibilityTilesInteractor.init(currentTilesInteractor)
autoAddInteractor.init(currentTilesInteractor)
restoreReconciliationInteractor.start()
+
+ if (NewQsUI.isEnabled) {
+ gridConsistencyInteractor.start()
+ }
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
new file mode 100644
index 0000000..db752dd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorTest.kt
@@ -0,0 +1,199 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.GridLayoutTypeRepository
+import com.android.systemui.qs.panels.data.repository.IconTilesRepository
+import com.android.systemui.qs.panels.data.repository.gridLayoutTypeRepository
+import com.android.systemui.qs.panels.data.repository.iconTilesRepository
+import com.android.systemui.qs.panels.shared.model.GridLayoutType
+import com.android.systemui.qs.panels.shared.model.InfiniteGridLayoutType
+import com.android.systemui.qs.pipeline.data.repository.tileSpecRepository
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class GridConsistencyInteractorTest : SysuiTestCase() {
+
+ data object TestGridLayoutType : GridLayoutType
+
+ private val gridLayout: MutableStateFlow<GridLayoutType> =
+ MutableStateFlow(InfiniteGridLayoutType)
+
+ private val iconOnlyTiles =
+ MutableStateFlow(
+ setOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ )
+ )
+
+ private val kosmos =
+ testKosmos().apply {
+ iconTilesRepository =
+ object : IconTilesRepository {
+ override val iconTilesSpecs: StateFlow<Set<TileSpec>>
+ get() = iconOnlyTiles.asStateFlow()
+ }
+ gridConsistencyInteractorsMap =
+ mapOf(
+ Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor),
+ Pair(TestGridLayoutType, noopGridConsistencyInteractor)
+ )
+ gridLayoutTypeRepository =
+ object : GridLayoutTypeRepository {
+ override val layout: StateFlow<GridLayoutType> = gridLayout.asStateFlow()
+ }
+ }
+
+ private val underTest = with(kosmos) { gridConsistencyInteractor }
+
+ @Before
+ fun setUp() {
+ gridLayout.value = InfiniteGridLayoutType
+ underTest.start()
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun changeLayoutType_usesCorrectGridConsistencyInteractor() =
+ with(kosmos) {
+ testScope.runTest {
+ // Using the no-op grid consistency interactor
+ gridLayout.value = TestGridLayoutType
+
+ // Setting an invalid layout with holes
+ // [ Large A ] [ sa ]
+ // [ Large B ] [ Large C ]
+ // [ sb ] [ Large D ]
+ val newTiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("smallA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeD"),
+ )
+ tileSpecRepository.setTiles(0, newTiles)
+
+ runCurrent()
+
+ val tiles = currentTilesInteractor.currentTiles.value
+ val tileSpecs = tiles.map { it.spec }
+
+ // Saved tiles should be unchanged
+ assertThat(tileSpecs).isEqualTo(newTiles)
+ }
+ }
+
+ @Test
+ fun validTilesWithInfiniteGridConsistencyInteractor_unchangedList() =
+ with(kosmos) {
+ testScope.runTest {
+ // Setting a valid layout with holes
+ // [ Large A ] [ sa ][ sb ]
+ // [ Large B ] [ Large C ]
+ // [ Large D ]
+ val newTiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("smallA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("largeD"),
+ )
+ tileSpecRepository.setTiles(0, newTiles)
+
+ runCurrent()
+
+ val tiles = currentTilesInteractor.currentTiles.value
+ val tileSpecs = tiles.map { it.spec }
+
+ // Saved tiles should be unchanged
+ assertThat(tileSpecs).isEqualTo(newTiles)
+ }
+ }
+
+ @Test
+ fun invalidTilesWithInfiniteGridConsistencyInteractor_savesNewList() =
+ with(kosmos) {
+ testScope.runTest {
+ // Setting an invalid layout with holes
+ // [ sa ] [ Large A ]
+ // [ Large B ] [ sb ] [ sc ]
+ // [ sd ] [ se ] [ Large C ]
+ val newTiles =
+ listOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("largeA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("smallB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ TileSpec.create("largeC"),
+ )
+ tileSpecRepository.setTiles(0, newTiles)
+
+ runCurrent()
+
+ val tiles = currentTilesInteractor.currentTiles.value
+ val tileSpecs = tiles.map { it.spec }
+
+ // Expected grid
+ // [ sa ] [ Large A ] [ sb ]
+ // [ Large B ] [ sc ] [ sd ]
+ // [ se ] [ Large C ]
+ val expectedTiles =
+ listOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("largeA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ TileSpec.create("largeC"),
+ )
+
+ // Saved tiles should be unchanged
+ assertThat(tileSpecs).isEqualTo(expectedTiles)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
new file mode 100644
index 0000000..bda48ad
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorTest.kt
@@ -0,0 +1,195 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.qs.panels.data.repository.IconTilesRepository
+import com.android.systemui.qs.panels.data.repository.iconTilesRepository
+import com.android.systemui.qs.pipeline.shared.TileSpec
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class InfiniteGridConsistencyInteractorTest : SysuiTestCase() {
+
+ private val iconOnlyTiles =
+ MutableStateFlow(
+ setOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ )
+ )
+ private val kosmos =
+ testKosmos().apply {
+ iconTilesRepository =
+ object : IconTilesRepository {
+ override val iconTilesSpecs: StateFlow<Set<TileSpec>>
+ get() = iconOnlyTiles.asStateFlow()
+ }
+ }
+ private val underTest = with(kosmos) { infiniteGridConsistencyInteractor }
+
+ @Test
+ fun validTiles_returnsUnchangedList() =
+ with(kosmos) {
+ testScope.runTest {
+ // Original grid
+ // [ Large A ] [ sa ][ sb ]
+ // [ Large B ] [ Large C ]
+ // [ Large D ]
+ val tiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("smallA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("largeD"),
+ )
+
+ val newTiles = underTest.reconcileTiles(tiles)
+
+ assertThat(newTiles).isEqualTo(tiles)
+ }
+ }
+
+ @Test
+ fun invalidTiles_moveIconTileForward() =
+ with(kosmos) {
+ testScope.runTest {
+ // Original grid
+ // [ Large A ] [ sa ]
+ // [ Large B ] [ Large C ]
+ // [ sb ] [ Large D ]
+ val tiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("smallA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeD"),
+ )
+ // Expected grid
+ // [ Large A ] [ sa ][ sb ]
+ // [ Large B ] [ Large C ]
+ // [ Large D ]
+ val expectedTiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("smallA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("largeD"),
+ )
+
+ val newTiles = underTest.reconcileTiles(tiles)
+
+ assertThat(newTiles).isEqualTo(expectedTiles)
+ }
+ }
+
+ @Test
+ fun invalidTiles_moveIconTileBack() =
+ with(kosmos) {
+ testScope.runTest {
+ // Original grid
+ // [ sa ] [ Large A ]
+ // [ Large B ] [ Large C ]
+ // [ Large D ]
+ val tiles =
+ listOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("largeA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("largeD"),
+ )
+ // Expected grid
+ // [ Large A ] [ Large B ]
+ // [ Large C ] [ Large D ]
+ // [ sa ]
+ val expectedTiles =
+ listOf(
+ TileSpec.create("largeA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("largeC"),
+ TileSpec.create("largeD"),
+ TileSpec.create("smallA"),
+ )
+
+ val newTiles = underTest.reconcileTiles(tiles)
+
+ assertThat(newTiles).isEqualTo(expectedTiles)
+ }
+ }
+
+ @Test
+ fun invalidTiles_multipleCorrections() =
+ with(kosmos) {
+ testScope.runTest {
+ // Original grid
+ // [ sa ] [ Large A ]
+ // [ Large B ] [ sb ] [ sc ]
+ // [ sd ] [ se ] [ Large C ]
+ val tiles =
+ listOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("largeA"),
+ TileSpec.create("largeB"),
+ TileSpec.create("smallB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ TileSpec.create("largeC"),
+ )
+ // Expected grid
+ // [ sa ] [ Large A ] [ sb ]
+ // [ Large B ] [ sc ] [ sd ]
+ // [ se ] [ Large C ]
+ val expectedTiles =
+ listOf(
+ TileSpec.create("smallA"),
+ TileSpec.create("largeA"),
+ TileSpec.create("smallB"),
+ TileSpec.create("largeB"),
+ TileSpec.create("smallC"),
+ TileSpec.create("smallD"),
+ TileSpec.create("smallE"),
+ TileSpec.create("largeC"),
+ )
+
+ val newTiles = underTest.reconcileTiles(tiles)
+
+ assertThat(newTiles).isEqualTo(expectedTiles)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
index 3f91122..d72630d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/QuickSettingsKosmos.kt
@@ -32,6 +32,7 @@
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.plugins.activityStarter
import com.android.systemui.plugins.qs.QSFactory
+import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractorImpl
import com.android.systemui.qs.footer.foregroundServicesRepository
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
@@ -48,7 +49,7 @@
QsEventLoggerFake(uiEventLoggerFake, instanceIdSequenceFake)
}
-var Kosmos.qsTileFactory by Fixture<QSFactory>()
+var Kosmos.qsTileFactory by Fixture<QSFactory> { FakeQSFactory(::tileCreator) }
val Kosmos.fgsManagerController by Fixture { FakeFgsManagerController() }
@@ -98,3 +99,7 @@
showPowerButton = true,
)
}
+
+private fun tileCreator(spec: String): QSTile {
+ return FakeQSTile(0).apply { tileSpec = spec }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
index f846d57..4acedaa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/GridLayoutTypeRepositoryKosmos.kt
@@ -18,4 +18,5 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.gridLayoutTypeRepository by Kosmos.Fixture { GridLayoutTypeRepository() }
+var Kosmos.gridLayoutTypeRepository: GridLayoutTypeRepository by
+ Kosmos.Fixture { GridLayoutTypeRepositoryImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconTilesRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconTilesRepositoryKosmos.kt
index 685e772..e40152a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconTilesRepositoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/IconTilesRepositoryKosmos.kt
@@ -18,4 +18,4 @@
import com.android.systemui.kosmos.Kosmos
-val Kosmos.iconTilesRepository by Kosmos.Fixture { IconTilesRepositoryImpl() }
+var Kosmos.iconTilesRepository: IconTilesRepository by Kosmos.Fixture { IconTilesRepositoryImpl() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
new file mode 100644
index 0000000..d8af3fa
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/data/repository/InfiniteGridSizeRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.qs.panels.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.infiniteGridSizeRepository by Kosmos.Fixture { InfiniteGridSizeRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorKosmos.kt
new file mode 100644
index 0000000..edbc4c1
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridConsistencyInteractorKosmos.kt
@@ -0,0 +1,34 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.core.FakeLogBuffer
+import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
+
+val Kosmos.gridConsistencyInteractor by
+ Kosmos.Fixture {
+ GridConsistencyInteractor(
+ gridLayoutTypeInteractor,
+ currentTilesInteractor,
+ gridConsistencyInteractorsMap,
+ noopGridConsistencyInteractor,
+ FakeLogBuffer.Factory.create(),
+ applicationCoroutineScope,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
index c951642..34e99d3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/GridLayoutTypeInteractorKosmos.kt
@@ -27,3 +27,6 @@
val Kosmos.gridLayoutMap: Map<GridLayoutType, GridLayout> by
Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridLayout)) }
+
+var Kosmos.gridConsistencyInteractorsMap: Map<GridLayoutType, GridTypeConsistencyInteractor> by
+ Kosmos.Fixture { mapOf(Pair(InfiniteGridLayoutType, infiniteGridConsistencyInteractor)) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt
new file mode 100644
index 0000000..7f387d7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridConsistencyInteractorKosmos.kt
@@ -0,0 +1,24 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.infiniteGridConsistencyInteractor by
+ Kosmos.Fixture {
+ InfiniteGridConsistencyInteractor(iconTilesInteractor, infiniteGridSizeInteractor)
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
index 1893c30..34b266a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridLayoutKosmos.kt
@@ -19,4 +19,5 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
-val Kosmos.infiniteGridLayout by Kosmos.Fixture { InfiniteGridLayout(iconTilesInteractor) }
+val Kosmos.infiniteGridLayout by
+ Kosmos.Fixture { InfiniteGridLayout(iconTilesInteractor, infiniteGridSizeInteractor) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
new file mode 100644
index 0000000..6e11977
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/InfiniteGridSizeInteractorKosmos.kt
@@ -0,0 +1,23 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.qs.panels.data.repository.infiniteGridSizeRepository
+
+val Kosmos.infiniteGridSizeInteractor by
+ Kosmos.Fixture { InfiniteGridSizeInteractor(infiniteGridSizeRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractorKosmos.kt
new file mode 100644
index 0000000..e3beff7
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/domain/interactor/NoopGridConsistencyInteractorKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * 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.qs.panels.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.noopGridConsistencyInteractor by Kosmos.Fixture { NoopGridConsistencyInteractor() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
index 5fd8762..9481fca 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/panels/ui/viewmodel/TileGridViewModelKosmos.kt
@@ -20,8 +20,7 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.qs.panels.domain.interactor.gridLayoutMap
import com.android.systemui.qs.panels.domain.interactor.gridLayoutTypeInteractor
-import com.android.systemui.qs.panels.domain.interactor.iconTilesInteractor
-import com.android.systemui.qs.panels.ui.compose.InfiniteGridLayout
+import com.android.systemui.qs.panels.domain.interactor.infiniteGridLayout
import com.android.systemui.qs.pipeline.domain.interactor.currentTilesInteractor
val Kosmos.tileGridViewModel by
@@ -30,7 +29,7 @@
gridLayoutTypeInteractor,
gridLayoutMap,
currentTilesInteractor,
- InfiniteGridLayout(iconTilesInteractor),
+ infiniteGridLayout,
applicationCoroutineScope,
)
}