Merge "Fix Drag and drop bug when moving a tile within the current list" into main
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
index 8b49d43..601779f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModelTest.kt
@@ -54,8 +54,10 @@
import com.android.systemui.settings.userTracker
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -103,9 +105,7 @@
appName2,
)
- private val underTest: EditModeViewModel by lazy {
- kosmos.editModeViewModel
- }
+ private val underTest: EditModeViewModel by lazy { kosmos.editModeViewModel }
@Before
fun setUp() {
@@ -461,31 +461,87 @@
}
@Test
- fun tileNotAvailable_notShowing() = with(kosmos) {
- testScope.runTest {
- val unavailableTile = "work"
- qsTileFactory = FakeQSFactory { spec ->
- FakeQSTile(userTracker.userId, spec != unavailableTile)
- }
- tileAvailabilityInteractorsMap = mapOf(
- unavailableTile to FakeTileAvailabilityInteractor(
- emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+ fun tileNotAvailable_notShowing() =
+ with(kosmos) {
+ testScope.runTest {
+ val unavailableTile = "work"
+ qsTileFactory = FakeQSFactory { spec ->
+ FakeQSTile(userTracker.userId, spec != unavailableTile)
+ }
+ tileAvailabilityInteractorsMap =
+ mapOf(
+ unavailableTile to
+ FakeTileAvailabilityInteractor(
+ emptyMap<Int, Flow<Boolean>>().withDefault { flowOf(false) }
+ )
)
- )
- val tiles by collectLastValue(underTest.tiles)
- val currentTiles =
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
mutableListOf(
- TileSpec.create("flashlight"),
- TileSpec.create("airplane"),
- TileSpec.create("alarm"),
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("alarm"),
)
- currentTilesInteractor.setTiles(currentTiles)
+ currentTilesInteractor.setTiles(currentTiles)
- underTest.startEditing()
+ underTest.startEditing()
- assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) }).isTrue()
+ assertThat(tiles!!.none { it.tileSpec == TileSpec.create(unavailableTile) })
+ .isTrue()
+ }
}
- }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun currentTiles_moveTileDown() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+ underTest.startEditing()
+ runCurrent()
+
+ // Move flashlight tile to index 3
+ underTest.addTile(TileSpec.create("flashlight"), 3)
+
+ assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec })
+ .containsExactly("airplane", "internet", "alarm", "flashlight")
+ .inOrder()
+ }
+ }
+
+ @OptIn(ExperimentalCoroutinesApi::class)
+ @Test
+ fun currentTiles_moveTileUp() =
+ with(kosmos) {
+ testScope.runTest {
+ val tiles by collectLastValue(underTest.tiles)
+ val currentTiles =
+ mutableListOf(
+ TileSpec.create("flashlight"),
+ TileSpec.create("airplane"),
+ TileSpec.create("internet"),
+ TileSpec.create("alarm"),
+ )
+ currentTilesInteractor.setTiles(currentTiles)
+ underTest.startEditing()
+ runCurrent()
+
+ // Move alarm tile to index 0
+ underTest.addTile(TileSpec.create("alarm"), 0)
+
+ assertThat(tiles!!.filter { it.isCurrent }.map { it.tileSpec.spec })
+ .containsExactly("alarm", "flashlight", "airplane", "internet")
+ .inOrder()
+ }
+ }
companion object {
private val drawable1 = TestStubDrawable("drawable1")
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
index 19b8c66..62bfc72 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditModeViewModel.kt
@@ -27,6 +27,8 @@
import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END
import com.android.systemui.qs.pipeline.domain.interactor.MinimumTilesInteractor
import com.android.systemui.qs.pipeline.shared.TileSpec
+import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -37,22 +39,20 @@
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import javax.inject.Inject
-import javax.inject.Named
@SysUISingleton
@OptIn(ExperimentalCoroutinesApi::class)
class EditModeViewModel
@Inject
constructor(
- private val editTilesListInteractor: EditTilesListInteractor,
- private val currentTilesInteractor: CurrentTilesInteractor,
- private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
- private val minTilesInteractor: MinimumTilesInteractor,
- @Named("Default") private val defaultGridLayout: GridLayout,
- @Application private val applicationScope: CoroutineScope,
- gridLayoutTypeInteractor: GridLayoutTypeInteractor,
- gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
+ private val editTilesListInteractor: EditTilesListInteractor,
+ private val currentTilesInteractor: CurrentTilesInteractor,
+ private val tilesAvailabilityInteractor: TilesAvailabilityInteractor,
+ private val minTilesInteractor: MinimumTilesInteractor,
+ @Named("Default") private val defaultGridLayout: GridLayout,
+ @Application private val applicationScope: CoroutineScope,
+ gridLayoutTypeInteractor: GridLayoutTypeInteractor,
+ gridLayoutMap: Map<GridLayoutType, @JvmSuppressWildcards GridLayout>,
) {
private val _isEditing = MutableStateFlow(false)
@@ -93,10 +93,12 @@
val editTilesData = editTilesListInteractor.getTilesToEdit()
// Query only the non current platform tiles, as any current tile is clearly
// available
- val unavailable = tilesAvailabilityInteractor.getUnavailableTiles(
- editTilesData.stockTiles.map { it.tileSpec }
- .minus(currentTilesInteractor.currentTilesSpecs.toSet())
- )
+ val unavailable =
+ tilesAvailabilityInteractor.getUnavailableTiles(
+ editTilesData.stockTiles
+ .map { it.tileSpec }
+ .minus(currentTilesInteractor.currentTilesSpecs.toSet())
+ )
currentTilesInteractor.currentTiles.map { tiles ->
val currentSpecs = tiles.map { it.spec }
val canRemoveTiles = currentSpecs.size > minimumTiles
@@ -106,28 +108,28 @@
val nonCurrentTiles = allTiles.filter { it.tileSpec !in currentSpecs }
(currentTiles + nonCurrentTiles)
- .filterNot { it.tileSpec in unavailable }
- .map {
- val current = it.tileSpec in currentSpecs
- val availableActions = buildSet {
- if (current) {
- add(AvailableEditActions.MOVE)
- if (canRemoveTiles) {
- add(AvailableEditActions.REMOVE)
- }
- } else {
- add(AvailableEditActions.ADD)
+ .filterNot { it.tileSpec in unavailable }
+ .map {
+ val current = it.tileSpec in currentSpecs
+ val availableActions = buildSet {
+ if (current) {
+ add(AvailableEditActions.MOVE)
+ if (canRemoveTiles) {
+ add(AvailableEditActions.REMOVE)
}
+ } else {
+ add(AvailableEditActions.ADD)
}
- EditTileViewModel(
- it.tileSpec,
- it.icon,
- it.label,
- it.appName,
- current,
- availableActions
- )
}
+ EditTileViewModel(
+ it.tileSpec,
+ it.icon,
+ it.label,
+ it.appName,
+ current,
+ availableActions
+ )
+ }
}
} else {
emptyFlow()
@@ -144,13 +146,16 @@
_isEditing.value = false
}
- /** Immediately moves [tileSpec] to [position]. */
- fun moveTile(tileSpec: TileSpec, position: Int) {
- throw NotImplementedError("This is not supported yet")
- }
-
- /** Immediately adds [tileSpec] to the current tiles at [position]. */
+ /**
+ * Immediately adds [tileSpec] to the current tiles at [position]. If the [tileSpec] was already
+ * present, it will be moved to the new position.
+ */
fun addTile(tileSpec: TileSpec, position: Int = POSITION_AT_END) {
+ // Removing tile if it's already present to insert it at the new index.
+ if (currentTilesInteractor.currentTilesSpecs.contains(tileSpec)) {
+ removeTile(tileSpec)
+ }
+
currentTilesInteractor.addTile(tileSpec, position)
}