Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 13601812 authored by Olivier St-Onge's avatar Olivier St-Onge
Browse files

Use the availability of a tile as a key for the grouped and sorted list in edit mode

This also passes the entire list of tiles to Edit mode instead of splitting and rejoining the list.
The AvailableTileGridCell data class is also removed as it's no longer needed.

Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Fixes: 405279487
Test: DragAndDropTest.kt
Test: EditModeTest.kt
Test: ResizingTest.kt
Change-Id: Ieb085569fe4d82bc9c7f21651ef8c44474f59ca4
parent 1caf584c
Loading
Loading
Loading
Loading
+28 −47
Original line number Original line Diff line number Diff line
@@ -85,7 +85,6 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.rememberCoroutineScope
@@ -156,7 +155,6 @@ import com.android.systemui.qs.panels.ui.compose.selection.TileState
import com.android.systemui.qs.panels.ui.compose.selection.rememberResizingState
import com.android.systemui.qs.panels.ui.compose.selection.rememberResizingState
import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.rememberSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
import com.android.systemui.qs.panels.ui.model.AvailableTileGridCell
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
@@ -229,7 +227,7 @@ private fun EditModeTopBar(onStopEditing: () -> Unit, onReset: (() -> Unit)?) {
@Composable
@Composable
fun DefaultEditTileGrid(
fun DefaultEditTileGrid(
    listState: EditTileListState,
    listState: EditTileListState,
    otherTiles: List<EditTileViewModel>,
    allTiles: List<EditTileViewModel>,
    modifier: Modifier,
    modifier: Modifier,
    onAddTile: (TileSpec, Int) -> Unit,
    onAddTile: (TileSpec, Int) -> Unit,
    onRemoveTile: (TileSpec) -> Unit,
    onRemoveTile: (TileSpec) -> Unit,
@@ -331,19 +329,8 @@ fun DefaultEditTileGrid(
                                spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
                                spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
                            modifier = modifier.fillMaxSize(),
                            modifier = modifier.fillMaxSize(),
                        ) {
                        ) {
                            val availableTiles = remember {
                                mutableStateListOf<AvailableTileGridCell>().apply {
                                    addAll(toAvailableTiles(listState.tiles, otherTiles))
                                }
                            }
                            LaunchedEffect(listState.tiles, otherTiles) {
                                availableTiles.apply {
                                    clear()
                                    addAll(toAvailableTiles(listState.tiles, otherTiles))
                                }
                            }
                            AvailableTileGrid(
                            AvailableTileGrid(
                                availableTiles,
                                allTiles,
                                selectionState,
                                selectionState,
                                listState.columns,
                                listState.columns,
                                { onAddTile(it, listState.tileSpecs().size) }, // Add to the end
                                { onAddTile(it, listState.tileSpecs().size) }, // Add to the end
@@ -544,17 +531,19 @@ private fun CurrentTilesGrid(


@Composable
@Composable
private fun AvailableTileGrid(
private fun AvailableTileGrid(
    tiles: List<AvailableTileGridCell>,
    tiles: List<EditTileViewModel>,
    selectionState: MutableSelectionState,
    selectionState: MutableSelectionState,
    columns: Int,
    columns: Int,
    onAddTile: (TileSpec) -> Unit,
    onAddTile: (TileSpec) -> Unit,
    dragAndDropState: DragAndDropState,
    dragAndDropState: DragAndDropState,
) {
) {
    // Available tiles aren't visible during drag and drop, so the row/col isn't needed
    // Group and sort to get the proper order tiles should be displayed in
    val groupedTiles =
    val groupedTileSpecs =
        remember(tiles.fastMap { it.tile.category }, tiles.fastMap { it.tile.label }) {
        remember(tiles.fastMap { it.category }, tiles.fastMap { it.label }) {
            groupAndSort(tiles)
            groupAndSort(tiles).mapValues { tiles -> tiles.value.map { it.tileSpec } }
        }
        }
    // Map of TileSpec to EditTileViewModel to use with the grouped tilespecs
    val viewModelsMap = remember(tiles) { tiles.associateBy { it.tileSpec } }


    // Available tiles
    // Available tiles
    Column(
    Column(
@@ -563,7 +552,7 @@ private fun AvailableTileGrid(
        modifier =
        modifier =
            Modifier.fillMaxWidth().wrapContentHeight().testTag(AVAILABLE_TILES_GRID_TEST_TAG),
            Modifier.fillMaxWidth().wrapContentHeight().testTag(AVAILABLE_TILES_GRID_TEST_TAG),
    ) {
    ) {
        groupedTiles.forEach { (category, tiles) ->
        groupedTileSpecs.forEach { (category, tileSpecs) ->
            key(category) {
            key(category) {
                val surfaceColor = MaterialTheme.colorScheme.surface
                val surfaceColor = MaterialTheme.colorScheme.surface
                Column(
                Column(
@@ -582,15 +571,16 @@ private fun AvailableTileGrid(
                        category,
                        category,
                        modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
                        modifier = Modifier.fillMaxWidth().padding(bottom = 16.dp),
                    )
                    )
                    tiles.chunked(columns).forEach { row ->
                    tileSpecs.chunked(columns).forEach { row ->
                        Row(
                        Row(
                            horizontalArrangement = spacedBy(TileArrangementPadding),
                            horizontalArrangement = spacedBy(TileArrangementPadding),
                            modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
                            modifier = Modifier.fillMaxWidth().height(IntrinsicSize.Max),
                        ) {
                        ) {
                            row.forEach { tileGridCell ->
                            for (tileSpec in row) {
                                key(tileGridCell.key) {
                                val viewModel = viewModelsMap[tileSpec] ?: continue
                                key(tileSpec) {
                                    AvailableTileGridCell(
                                    AvailableTileGridCell(
                                        cell = tileGridCell,
                                        cell = viewModel,
                                        dragAndDropState = dragAndDropState,
                                        dragAndDropState = dragAndDropState,
                                        selectionState = selectionState,
                                        selectionState = selectionState,
                                        onAddTile = onAddTile,
                                        onAddTile = onAddTile,
@@ -855,17 +845,17 @@ private fun CategoryHeader(category: TileCategory, modifier: Modifier = Modifier


@Composable
@Composable
private fun AvailableTileGridCell(
private fun AvailableTileGridCell(
    cell: AvailableTileGridCell,
    cell: EditTileViewModel,
    dragAndDropState: DragAndDropState,
    dragAndDropState: DragAndDropState,
    selectionState: MutableSelectionState,
    selectionState: MutableSelectionState,
    onAddTile: (TileSpec) -> Unit,
    onAddTile: (TileSpec) -> Unit,
    modifier: Modifier = Modifier,
    modifier: Modifier = Modifier,
) {
) {
    val stateDescription: String? =
    val stateDescription: String? =
        if (cell.isAvailable) null
        if (cell.isCurrent) stringResource(R.string.accessibility_qs_edit_tile_already_added)
        else stringResource(R.string.accessibility_qs_edit_tile_already_added)
        else null


    val alpha by animateFloatAsState(if (cell.isAvailable) 1f else .38f)
    val alpha by animateFloatAsState(if (cell.isCurrent) .38f else 1f)
    val colors = EditModeTileDefaults.editTileColors()
    val colors = EditModeTileDefaults.editTileColors()


    // Displays the tile as an icon tile with the label underneath
    // Displays the tile as an icon tile with the label underneath
@@ -881,21 +871,21 @@ private fun AvailableTileGridCell(
    ) {
    ) {
        Box(Modifier.fillMaxWidth().height(TileHeight)) {
        Box(Modifier.fillMaxWidth().height(TileHeight)) {
            val draggableModifier =
            val draggableModifier =
                if (cell.isAvailable) {
                if (cell.isCurrent) {
                    Modifier
                } else {
                    Modifier.dragAndDropTileSource(
                    Modifier.dragAndDropTileSource(
                        SizedTileImpl(cell.tile, cell.width),
                        SizedTileImpl(cell, 1), // Available tiles are fixed at a width of 1
                        dragAndDropState,
                        dragAndDropState,
                        DragType.Add,
                        DragType.Add,
                    ) {
                    ) {
                        selectionState.unSelect()
                        selectionState.unSelect()
                    }
                    }
                } else {
                    Modifier
                }
                }
            Box(draggableModifier.fillMaxSize().tileBackground { colors.background }) {
            Box(draggableModifier.fillMaxSize().tileBackground { colors.background }) {
                // Icon
                // Icon
                SmallTileContent(
                SmallTileContent(
                    iconProvider = { cell.tile.icon },
                    iconProvider = { cell.icon },
                    color = colors.icon,
                    color = colors.icon,
                    animateToEnd = true,
                    animateToEnd = true,
                    modifier = Modifier.align(Alignment.Center),
                    modifier = Modifier.align(Alignment.Center),
@@ -906,15 +896,15 @@ private fun AvailableTileGridCell(
                icon = Icons.Default.Add,
                icon = Icons.Default.Add,
                contentDescription =
                contentDescription =
                    stringResource(id = R.string.accessibility_qs_edit_tile_add_action),
                    stringResource(id = R.string.accessibility_qs_edit_tile_add_action),
                enabled = cell.isAvailable,
                enabled = !cell.isCurrent,
            ) {
            ) {
                onAddTile(cell.tile.tileSpec)
                onAddTile(cell.tileSpec)
                selectionState.select(cell.tile.tileSpec)
                selectionState.select(cell.tileSpec)
            }
            }
        }
        }
        Box(Modifier.fillMaxSize()) {
        Box(Modifier.fillMaxSize()) {
            Text(
            Text(
                cell.tile.label.text,
                cell.label.text,
                maxLines = 2,
                maxLines = 2,
                color = colors.label,
                color = colors.label,
                overflow = TextOverflow.Ellipsis,
                overflow = TextOverflow.Ellipsis,
@@ -998,15 +988,6 @@ fun EditTile(
    }
    }
}
}


private fun toAvailableTiles(
    currentTiles: List<GridCell>,
    otherTiles: List<EditTileViewModel>,
): List<AvailableTileGridCell> {
    return currentTiles.filterIsInstance<TileGridCell>().fastMap {
        AvailableTileGridCell(it.tile, isAvailable = false)
    } + otherTiles.fastMap { AvailableTileGridCell(it) }
}

private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float {
private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float {
    return (containerSize - ToggleTargetSize.roundToPx()) / 2f -
    return (containerSize - ToggleTargetSize.roundToPx()) / 2f -
        CommonTileDefaults.TileStartPadding.toPx()
        CommonTileDefaults.TileStartPadding.toPx()
+2 −2
Original line number Original line Diff line number Diff line
@@ -162,7 +162,7 @@ constructor(
        val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
        val largeTilesSpan by iconTilesViewModel.largeTilesSpanState
        val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()
        val largeTiles by iconTilesViewModel.largeTiles.collectAsStateWithLifecycle()


        val (currentTiles, otherTiles) = tiles.partition { it.isCurrent }
        val currentTiles = tiles.filter { it.isCurrent }
        val listState =
        val listState =
            remember(columns, largeTilesSpan) {
            remember(columns, largeTilesSpan) {
                EditTileListState(
                EditTileListState(
@@ -176,7 +176,7 @@ constructor(


        DefaultEditTileGrid(
        DefaultEditTileGrid(
            listState = listState,
            listState = listState,
            otherTiles = otherTiles,
            allTiles = tiles,
            modifier = modifier,
            modifier = modifier,
            onAddTile = onAddTile,
            onAddTile = onAddTile,
            onRemoveTile = onRemoveTile,
            onRemoveTile = onRemoveTile,
+0 −13
Original line number Original line Diff line number Diff line
@@ -21,7 +21,6 @@ import androidx.compose.runtime.Immutable
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
import com.android.systemui.qs.panels.shared.model.splitInRowsSequence
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.pipeline.shared.TileSpec
import com.android.systemui.qs.shared.model.CategoryAndName
import com.android.systemui.qs.shared.model.CategoryAndName


/** Represents an item from a grid associated with a row and a span */
/** Represents an item from a grid associated with a row and a span */
@@ -51,18 +50,6 @@ data class TileGridCell(
    ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width)
    ) : this(tile = sizedTile.tile, row = row, column = column, width = sizedTile.width)
}
}


/**
 * Represents a [EditTileViewModel] from the edit mode available tiles grid and whether it is
 * available to add or not.
 */
@Immutable
data class AvailableTileGridCell(
    override val tile: EditTileViewModel,
    override val width: Int = 1,
    val isAvailable: Boolean = true,
    val key: TileSpec = tile.tileSpec,
) : SizedTile<EditTileViewModel>, CategoryAndName by tile

/** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
/** Represents an empty space used to fill incomplete rows. Will always display as a 1x1 tile */
@Immutable
@Immutable
data class SpacerGridCell(
data class SpacerGridCell(
+1 −1
Original line number Original line Diff line number Diff line
@@ -54,7 +54,7 @@ class DragAndDropTest : SysuiTestCase() {
        PlatformTheme {
        PlatformTheme {
            DefaultEditTileGrid(
            DefaultEditTileGrid(
                listState = listState,
                listState = listState,
                otherTiles = listOf(),
                allTiles = listState.tiles.filterIsInstance<TileGridCell>().map { it.tile },
                modifier = Modifier.fillMaxSize(),
                modifier = Modifier.fillMaxSize(),
                onAddTile = { _, _ -> },
                onAddTile = { _, _ -> },
                onRemoveTile = {},
                onRemoveTile = {},
+6 −6
Original line number Original line Diff line number Diff line
@@ -52,9 +52,9 @@ class EditModeTest : SysuiTestCase() {
    @get:Rule val composeRule = createComposeRule()
    @get:Rule val composeRule = createComposeRule()


    @Composable
    @Composable
    private fun EditTileGridUnderTest(tiles: List<EditTileViewModel>) {
    private fun EditTileGridUnderTest() {
        val allTiles = remember { tiles.toMutableStateList() }
        val allTiles = remember { TestEditTiles.toMutableStateList() }
        val (currentTiles, otherTiles) = allTiles.partition { it.isCurrent }
        val currentTiles = allTiles.filter { it.isCurrent }
        val listState =
        val listState =
            EditTileListState(currentTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
            EditTileListState(currentTiles, TestLargeTilesSpecs, columns = 4, largeTilesSpan = 2)
        LaunchedEffect(currentTiles) { listState.updateTiles(currentTiles, TestLargeTilesSpecs) }
        LaunchedEffect(currentTiles) { listState.updateTiles(currentTiles, TestLargeTilesSpecs) }
@@ -62,7 +62,7 @@ class EditModeTest : SysuiTestCase() {
        PlatformTheme {
        PlatformTheme {
            DefaultEditTileGrid(
            DefaultEditTileGrid(
                listState = listState,
                listState = listState,
                otherTiles = otherTiles,
                allTiles = allTiles,
                modifier = Modifier.fillMaxSize(),
                modifier = Modifier.fillMaxSize(),
                onAddTile = { spec, _ ->
                onAddTile = { spec, _ ->
                    val index = allTiles.indexOfFirst { it.tileSpec == spec }
                    val index = allTiles.indexOfFirst { it.tileSpec == spec }
@@ -82,7 +82,7 @@ class EditModeTest : SysuiTestCase() {


    @Test
    @Test
    fun clickAvailableTile_shouldAdd() {
    fun clickAvailableTile_shouldAdd() {
        composeRule.setContent { EditTileGridUnderTest(TestEditTiles) }
        composeRule.setContent { EditTileGridUnderTest() }
        composeRule.waitForIdle()
        composeRule.waitForIdle()


        composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add
        composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add
@@ -96,7 +96,7 @@ class EditModeTest : SysuiTestCase() {


    @Test
    @Test
    fun placementMode_shouldRepositionTile() {
    fun placementMode_shouldRepositionTile() {
        composeRule.setContent { EditTileGridUnderTest(TestEditTiles) }
        composeRule.setContent { EditTileGridUnderTest() }
        composeRule.waitForIdle()
        composeRule.waitForIdle()


        // Double tap first "tileA", i.e. the one in the current grid
        // Double tap first "tileA", i.e. the one in the current grid
Loading