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

Commit f759f9c9 authored by Olivier St-Onge's avatar Olivier St-Onge Committed by Android (Google) Code Review
Browse files

Merge "Cleaning up edit mode tabs code" into main

parents 37949889 55463ac1
Loading
Loading
Loading
Loading
+0 −7
Original line number Diff line number Diff line
@@ -980,13 +980,6 @@ flag {
    }
}

flag {
    name: "qs_edit_mode_tabs"
    namespace: "systemui"
    description: "Splits the Quick Settings edit mode in tabs. This feature is for the compose version of Quick Settings."
    bug: "416236871"
}

flag {
    name: "qs_edit_mode_v2"
    namespace: "systemui"
+0 −47
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.flags

import com.android.systemui.Flags
import com.android.systemui.flags.RefactorFlagUtils

/** Object to help check if the Quick Settings edit mode should use tabs. */
object QsEditModeTabs {
    /** The aconfig flag name */
    const val FLAG_NAME = Flags.FLAG_QS_EDIT_MODE_TABS

    /** Should edit mode use tabs */
    @JvmStatic
    inline val isEnabled
        get() = Flags.qsEditModeTabs()

    /**
     * Called to ensure code is only run when the flag is enabled. This protects users from the
     * unintended behaviors caused by accidentally running new logic, while also crashing on an eng
     * build to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun isUnexpectedlyInLegacyMode() =
        RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)

    /**
     * Called to ensure code is only run when the flag is disabled. This will throw an exception if
     * the flag is enabled to ensure that the refactor author catches issues in testing.
     */
    @JvmStatic
    inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
}
+29 −135
Original line number Diff line number Diff line
@@ -169,7 +169,6 @@ import com.android.systemui.common.ui.compose.load
import com.android.systemui.common.ui.icons.MoreVert
import com.android.systemui.common.ui.icons.Undo
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.flags.QsEditModeTabs
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.DragAndDropState
import com.android.systemui.qs.panels.ui.compose.DragType
@@ -200,11 +199,9 @@ 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.rememberSelectionState
import com.android.systemui.qs.panels.ui.compose.selection.selectableTile
import com.android.systemui.qs.panels.ui.compose.tabs.EditModeTabs
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.TileGridCell
import com.android.systemui.qs.panels.ui.viewmodel.EditModeTabViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModelConstants.APP_ICON_INLINE_CONTENT_ID
import com.android.systemui.qs.panels.ui.viewmodel.EditTopBarActionViewModel
@@ -465,34 +462,6 @@ fun DefaultEditTileGrid(
                }
            }

            if (QsEditModeTabs.isEnabled) {
                val editModeTabViewModel = remember { EditModeTabViewModel() }
                EditModeScrollableColumnWithTabs(
                    listState = listState,
                    selectionState = selectionState,
                    innerPadding = innerPadding,
                    scrollState = scrollState,
                    onEditAction = onEditAction,
                    editModeTabViewModel = editModeTabViewModel,
                ) {
                    CurrentTilesGrid(
                        listState = listState,
                        selectionState = selectionState,
                        canRemoveTiles = editModeTabViewModel.selectedTab.isTilesEditingAllowed,
                        canLayoutTiles = editModeTabViewModel.selectedTab.isTilesLayoutAllowed,
                        onEditAction = onEditAction,
                    )

                    AnimatedAvailableTilesGrid(
                        allTiles = allTiles,
                        listState = listState,
                        selectionState = selectionState,
                        onEditAction = onEditAction,
                        canLayoutTile = editModeTabViewModel.selectedTab.isTilesLayoutAllowed,
                        showAvailableTiles = editModeTabViewModel.selectedTab.isTilesEditingAllowed,
                    )
                }
            } else {
            EditModeScrollableColumn(
                listState = listState,
                selectionState = selectionState,
@@ -509,8 +478,6 @@ fun DefaultEditTileGrid(
                CurrentTilesGrid(
                    listState = listState,
                    selectionState = selectionState,
                        canRemoveTiles = true,
                        canLayoutTiles = true,
                    onEditAction = onEditAction,
                )

@@ -530,7 +497,6 @@ fun DefaultEditTileGrid(
        }
    }
}
}

@Composable
private fun EditModeScrollableColumn(
@@ -569,64 +535,6 @@ private fun EditModeScrollableColumn(
    }
}

@Composable
private fun EditModeScrollableColumnWithTabs(
    listState: EditTileListState,
    selectionState: MutableSelectionState,
    innerPadding: PaddingValues,
    scrollState: ScrollState,
    onEditAction: (EditAction) -> Unit,
    editModeTabViewModel: EditModeTabViewModel,
    modifier: Modifier = Modifier,
    content: @Composable ColumnScope.() -> Unit,
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
        modifier =
            modifier
                .fillMaxSize()
                // Apply top padding before the scroll so the scrollable doesn't show under
                // the top bar
                .padding(top = innerPadding.calculateTopPadding()),
    ) {
        Column(
            verticalArrangement =
                spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
            modifier =
                Modifier.weight(1f)
                    .fillMaxWidth()
                    .clipScrollableContainer(Orientation.Vertical)
                    .clip(RoundedCornerShape(GridBackgroundCornerRadius))
                    .verticalScroll(scrollState)
                    .dragAndDropRemoveZone(listState) { spec, removalEnabled ->
                        if (removalEnabled) {
                            // If removal is enabled, remove the tile
                            onEditAction(EditAction.RemoveTile(spec))
                        } else {
                            // Otherwise submit the new tile ordering
                            onEditAction(EditAction.SetTiles(listState.tileSpecs()))
                            selectionState.select(spec)
                        }
                    },
        ) {
            TabGridHeader(
                editModeTabViewModel.selectedTab.headerResId,
                modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp),
            )

            content()
        }

        // Disable tab selection while a drag is in progress
        EditModeTabs(editModeTabViewModel, enabled = !listState.dragInProgress) {
            selectionState.unSelect()
        }

        Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
    }
}

@Composable
private fun AutoScrollGrid(
    listState: EditTileListState,
@@ -760,8 +668,6 @@ private fun EditGridCenteredText(text: String, modifier: Modifier = Modifier) {
private fun CurrentTilesGrid(
    listState: EditTileListState,
    selectionState: MutableSelectionState,
    canRemoveTiles: Boolean,
    canLayoutTiles: Boolean,
    onEditAction: (EditAction) -> Unit,
) {
    val currentListState by rememberUpdatedState(listState)
@@ -808,8 +714,6 @@ private fun CurrentTilesGrid(
            listState = listState,
            selectionState = selectionState,
            gridState = gridState,
            canRemoveTiles = canRemoveTiles,
            canLayoutTiles = canLayoutTiles,
            coroutineScope = coroutineScope,
            onRemoveTile = { onEditAction(EditAction.RemoveTile(it)) },
        ) { resizingOperation ->
@@ -989,8 +893,6 @@ private fun GridCell.key(index: Int): Any {
 * @param listState the [EditTileListState] for this grid
 * @param selectionState the [MutableSelectionState] for this grid
 * @param gridState the [LazyGridState] for this grid
 * @param canRemoveTiles whether tiles can be removed from this grid
 * @param canLayoutTiles whether tiles can be reordered/resized
 * @param coroutineScope the [CoroutineScope] to be used for the tiles
 * @param onRemoveTile the callback when a tile is removed from this grid
 * @param onResize the callback when a tile has a new [ResizeOperation]
@@ -999,8 +901,6 @@ fun LazyGridScope.EditTiles(
    listState: EditTileListState,
    selectionState: MutableSelectionState,
    gridState: LazyGridState,
    canRemoveTiles: Boolean,
    canLayoutTiles: Boolean,
    coroutineScope: CoroutineScope,
    onRemoveTile: (TileSpec) -> Unit,
    onResize: (operation: ResizeOperation) -> Unit,
@@ -1031,8 +931,6 @@ fun LazyGridScope.EditTiles(
                        dragAndDropState = listState,
                        selectionState = selectionState,
                        gridState = gridState,
                        canRemoveTile = canRemoveTiles,
                        canLayoutTile = canLayoutTiles,
                        onResize = onResize,
                        onRemoveTile = onRemoveTile,
                        coroutineScope = coroutineScope,
@@ -1053,14 +951,12 @@ fun LazyGridScope.EditTiles(
private fun rememberTileState(
    tile: EditTileViewModel,
    selectionState: MutableSelectionState,
    canRemoveTile: Boolean,
): State<TileState> {
    val tileState = remember { mutableStateOf(TileState.None) }
    val canShowRemovalBadge = canRemoveTile && tile.isRemovable

    LaunchedEffect(selectionState.selection, selectionState.placementEnabled, canShowRemovalBadge) {
    LaunchedEffect(selectionState.selection, selectionState.placementEnabled, tile.isRemovable) {
        tileState.value =
            selectionState.tileStateFor(tile.tileSpec, tileState.value, canShowRemovalBadge)
            selectionState.tileStateFor(tile.tileSpec, tileState.value, tile.isRemovable)
    }

    return tileState
@@ -1073,8 +969,6 @@ private fun LazyGridItemScope.TileGridCell(
    dragAndDropState: DragAndDropState,
    selectionState: MutableSelectionState,
    gridState: LazyGridState,
    canRemoveTile: Boolean,
    canLayoutTile: Boolean,
    onResize: (operation: ResizeOperation) -> Unit,
    onRemoveTile: (TileSpec) -> Unit,
    coroutineScope: CoroutineScope,
@@ -1082,7 +976,7 @@ private fun LazyGridItemScope.TileGridCell(
    modifier: Modifier = Modifier,
) {
    val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1)
    val tileState by rememberTileState(cell.tile, selectionState, canRemoveTile)
    val tileState by rememberTileState(cell.tile, selectionState)
    val resizingState = rememberResizingState(cell.tile.tileSpec, cell.isIcon)
    val progress: () -> Float = {
        if (tileState == TileState.Selected) {
@@ -1174,8 +1068,8 @@ private fun LazyGridItemScope.TileGridCell(
        // usually happens when resizing a tile multiple times. We can fix this by applying the
        // draggable modifier after the first frame
        var isSelectable by remember { mutableStateOf(false) }
        LaunchedEffect(canLayoutTile, dragAndDropState.dragInProgress) {
            isSelectable = canLayoutTile && !dragAndDropState.dragInProgress
        LaunchedEffect(dragAndDropState.dragInProgress) {
            isSelectable = !dragAndDropState.dragInProgress
        }
        val selectableModifier = Modifier.selectableTile(cell.tile.tileSpec, selectionState)
        val draggableModifier =
+0 −15
Original line number Diff line number Diff line
@@ -31,13 +31,11 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.common.ui.icons.Reset
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.grid.ui.compose.VerticalSpannedGrid
import com.android.systemui.haptics.msdl.qs.TileHapticsViewModelFactoryProvider
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.qs.flags.QSMaterialExpressiveTiles
import com.android.systemui.qs.flags.QsEditModeTabs
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
import com.android.systemui.qs.panels.ui.compose.ButtonGroupGrid
import com.android.systemui.qs.panels.ui.compose.EditTileListState
@@ -47,7 +45,6 @@ import com.android.systemui.qs.panels.ui.compose.bounceableInfo
import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.EditTopBarActionViewModel
import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel
import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TextFeedbackContentViewModel
@@ -205,18 +202,6 @@ constructor(
            }
        val actions =
            remember(topBarActionsViewModel) { topBarActionsViewModel.actions.toMutableStateList() }
        if (QsEditModeTabs.isEnabled) {
            val resetClick by rememberUpdatedState(dialogDelegate::showDialog)
            val resetAction = remember {
                EditTopBarActionViewModel(
                    Reset,
                    com.android.internal.R.string.reset,
                    { resetClick() },
                )
            }

            LaunchedEffect(actions) { actions.add(resetAction) }
        }
        val columns = columnsViewModel.columns
        val largeTilesSpan = columnsViewModel.largeSpan
        val largeTiles by viewModel.iconTilesViewModel.largeTilesState
+0 −121
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.ui.compose.tabs

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateColor
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandIn
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.BasicText
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FloatingToolbarDefaults
import androidx.compose.material3.HorizontalFloatingToolbar
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.android.compose.modifiers.padding
import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.qs.panels.ui.viewmodel.EditModeTabViewModel

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun EditModeTabs(
    viewModel: EditModeTabViewModel,
    enabled: Boolean,
    modifier: Modifier = Modifier,
    onTabChanged: () -> Unit = {},
) {
    val containerColor = LocalAndroidColorScheme.current.surfaceEffect1
    val selectedButtonColor = MaterialTheme.colorScheme.secondary
    val selectedTextColor = MaterialTheme.colorScheme.onSecondary
    val unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
    HorizontalFloatingToolbar(
        modifier = modifier.height(60.dp),
        expanded = false,
        contentPadding = PaddingValues(horizontal = 7.dp, vertical = 8.dp),
        colors =
            FloatingToolbarDefaults.standardFloatingToolbarColors(
                toolbarContainerColor = containerColor
            ),
    ) {
        EditModeTabViewModel.tabs.forEach { tab ->
            val isSelected = updateTransition(viewModel.selectedTab == tab)
            val selectionBackgroundAlpha by isSelected.animateFloat { if (it) 1f else 0f }
            val textColor by
                isSelected.animateColor {
                    if (it) {
                        selectedTextColor
                    } else {
                        unselectedTextColor
                    }
                }
            Row(
                horizontalArrangement = Arrangement.Center,
                verticalAlignment = Alignment.CenterVertically,
                modifier =
                    Modifier.fillMaxHeight()
                        .clickable(enabled = enabled) {
                            if (!isSelected.currentState) {
                                onTabChanged()
                            }
                            viewModel.selectTab(tab)
                        }
                        .padding(horizontal = 5.dp)
                        .drawBehind {
                            drawRoundRect(
                                color = selectedButtonColor,
                                alpha = selectionBackgroundAlpha,
                                cornerRadius = CornerRadius(size.height / 2),
                            )
                        }
                        .padding(horizontal = 16.dp),
            ) {
                isSelected.AnimatedVisibility(
                    visible = { it },
                    enter = (fadeIn() + expandIn(expandFrom = Alignment.Center)),
                    exit = (fadeOut() + shrinkOut(shrinkTowards = Alignment.Center)),
                ) {
                    Icon(
                        imageVector = tab.titleIcon,
                        contentDescription = null,
                        tint = MaterialTheme.colorScheme.onSecondary,
                        modifier = Modifier.padding(end = 8.dp),
                    )
                }
                BasicText(stringResource(tab.titleResId), color = { textColor })
            }
        }
    }
}
Loading