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

Commit b8178230 authored by Lucas Silva's avatar Lucas Silva
Browse files

Allow widgets to be resized on glanceable hub

Adds resize frame around the widget content of the hub in edit mode,
and implements resize logic in ContentListState

Bug: 368056517
Test: manually on device by expanding and shrinking widgets
Flag: com.android.systemui.communal_widget_resizing
Change-Id: I3ecba4418fa726dc438134db87cfd2dea0b1041f
parent e7e595de
Loading
Loading
Loading
Loading
+80 −25
Original line number Diff line number Diff line
@@ -148,6 +148,7 @@ import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
@@ -165,6 +166,7 @@ import com.android.compose.ui.graphics.painter.rememberDrawablePainter
import com.android.internal.R.dimen.system_app_widget_background_radius
import com.android.systemui.Flags
import com.android.systemui.Flags.communalTimerFlickerFix
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -176,6 +178,7 @@ import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSe
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.util.DensityUtils.Companion.adjustedDp
import com.android.systemui.communal.widgets.SmartspaceAppWidgetHostView
import com.android.systemui.communal.widgets.WidgetConfigurator
@@ -639,6 +642,38 @@ private fun ObserveNewWidgetAddedEffect(
    }
}

@Composable
private fun ResizableItemFrameWrapper(
    key: String,
    gridState: LazyGridState,
    minItemSpan: Int,
    gridContentPadding: PaddingValues,
    verticalArrangement: Arrangement.Vertical,
    enabled: Boolean,
    modifier: Modifier = Modifier,
    alpha: () -> Float = { 1f },
    onResize: (info: ResizeInfo) -> Unit = {},
    content: @Composable (modifier: Modifier) -> Unit,
) {
    if (!communalWidgetResizing()) {
        content(modifier)
    } else {
        ResizableItemFrame(
            key = key,
            gridState = gridState,
            minItemSpan = minItemSpan,
            gridContentPadding = gridContentPadding,
            verticalArrangement = verticalArrangement,
            enabled = enabled,
            alpha = alpha,
            modifier = modifier,
            onResize = onResize,
        ) {
            content(Modifier)
        }
    }
}

@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun BoxScope.CommunalHubLazyGrid(
@@ -695,13 +730,14 @@ private fun BoxScope.CommunalHubLazyGrid(
        gridModifier = gridModifier.height(hubDimensions.GridHeight)
    }

    val itemArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing)
    LazyHorizontalGrid(
        modifier = gridModifier,
        state = gridState,
        rows = GridCells.Fixed(CommunalContentSize.FULL.span),
        contentPadding = contentPadding,
        horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
        verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
        horizontalArrangement = itemArrangement,
        verticalArrangement = itemArrangement,
    ) {
        itemsIndexed(
            items = list,
@@ -710,25 +746,43 @@ private fun BoxScope.CommunalHubLazyGrid(
            span = { _, item -> GridItemSpan(item.size.span) },
        ) { index, item ->
            val size = SizeF(Dimensions.CardWidth.value, item.size.dp().value)
            val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
            if (viewModel.isEditMode && dragDropState != null) {
            val selected = item.key == selectedKey.value
                DraggableItem(
            val dpSize = DpSize(size.width.dp, size.height.dp)

            if (viewModel.isEditMode && dragDropState != null) {
                val outlineAlpha by
                    animateFloatAsState(
                        targetValue = if (selected) 1f else 0f,
                        animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
                        label = "Widget resizing outline alpha",
                    )
                ResizableItemFrameWrapper(
                    key = item.key,
                    gridState = gridState,
                    minItemSpan = CommunalContentSize.HALF.span,
                    gridContentPadding = contentPadding,
                    verticalArrangement = itemArrangement,
                    enabled = selected,
                    alpha = { outlineAlpha },
                    modifier =
                        if (dragDropState.draggingItemIndex == index) {
                            Modifier
                        } else {
                        Modifier.requiredSize(dpSize).thenIf(
                            dragDropState.draggingItemIndex != index
                        ) {
                            Modifier.animateItem(
                                placementSpec = spring(stiffness = Spring.StiffnessMediumLow)
                            )
                        },
                    onResize = { resizeInfo -> contentListState.resize(index, resizeInfo) },
                ) { modifier ->
                    DraggableItem(
                        modifier = modifier,
                        dragDropState = dragDropState,
                        selected = selected,
                        enabled = item.isWidgetContent(),
                        index = index,
                    ) { isDragging ->
                        CommunalContent(
                        modifier = cardModifier,
                            modifier = Modifier.fillMaxSize(),
                            model = item,
                            viewModel = viewModel,
                            size = size,
@@ -740,13 +794,14 @@ private fun BoxScope.CommunalHubLazyGrid(
                            widgetSection = widgetSection,
                        )
                    }
                }
            } else {
                CommunalContent(
                    model = item,
                    viewModel = viewModel,
                    size = size,
                    selected = false,
                    modifier = cardModifier.animateItem(),
                    modifier = Modifier.requiredSize(dpSize).animateItem(),
                    index = index,
                    contentListState = contentListState,
                    interactionHandler = interactionHandler,
+38 −7
Original line number Diff line number Diff line
@@ -21,8 +21,12 @@ import android.os.UserHandle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.toMutableStateList
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
import com.android.systemui.communal.ui.viewmodel.DragHandle
import com.android.systemui.communal.ui.viewmodel.ResizeInfo
import com.android.systemui.communal.widgets.WidgetConfigurator

@Composable
@@ -35,15 +39,11 @@ fun rememberContentListState(
        ContentListState(
            communalContent,
            { componentName, user, rank ->
                viewModel.onAddWidget(
                    componentName,
                    user,
                    rank,
                    widgetConfigurator,
                )
                viewModel.onAddWidget(componentName, user, rank, widgetConfigurator)
            },
            viewModel::onDeleteWidget,
            viewModel::onReorderWidgets,
            viewModel::onResizeWidget,
        )
    }
}
@@ -59,6 +59,7 @@ internal constructor(
    private val onAddWidget: (componentName: ComponentName, user: UserHandle, rank: Int) -> Unit,
    private val onDeleteWidget: (id: Int, componentName: ComponentName, rank: Int) -> Unit,
    private val onReorderWidgets: (widgetIdToRankMap: Map<Int, Int>) -> Unit,
    private val onResizeWidget: (id: Int, spanY: Int, widgetIdToRankMap: Map<Int, Int>) -> Unit,
) {
    var list = communalContent.toMutableStateList()
        private set
@@ -77,6 +78,36 @@ internal constructor(
        }
    }

    /** Resize a widget, possibly re-ordering widgets if needed. */
    fun resize(index: Int, resizeInfo: ResizeInfo) {
        val item = list[index]
        val currentSpan = item.size.span
        val newSpan = currentSpan + resizeInfo.spans
        // Only widgets can be resized
        if (
            !communalWidgetResizing() ||
                currentSpan == newSpan ||
                item !is CommunalContentModel.WidgetContent.Widget
        ) {
            return
        }
        list[index] = item.copy(size = CommunalContentSize.toSize(newSpan))
        val prevItem = list.getOrNull(index - 1)
        // Check if we have to update indices of items to accommodate the resize.
        val widgetIdToRankMap: Map<Int, Int> =
            if (
                resizeInfo.isExpanding &&
                    resizeInfo.fromHandle == DragHandle.TOP &&
                    prevItem is CommunalContentModel.WidgetContent.Widget
            ) {
                onMove(index - 1, index)
                mapOf(prevItem.appWidgetId to index, item.appWidgetId to index - 1)
            } else {
                emptyMap()
            }
        onResizeWidget(item.appWidgetId, newSpan, widgetIdToRankMap)
    }

    /**
     * Persists the new order with all the movements happened during drag operations & the new
     * widget drop (if applicable).
@@ -91,7 +122,7 @@ internal constructor(
    fun onSaveList(
        newItemComponentName: ComponentName? = null,
        newItemUser: UserHandle? = null,
        newItemIndex: Int? = null
        newItemIndex: Int? = null,
    ) {
        // New widget added to the grid. Other widgets are shifted as needed at the database level.
        if (newItemComponentName != null && newItemUser != null && newItemIndex != null) {
+16 −10
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.toOffset
import androidx.compose.ui.unit.toSize
import com.android.systemui.Flags.communalWidgetResizing
import com.android.systemui.communal.ui.compose.extensions.firstItemAtOffset
import com.android.systemui.communal.ui.compose.extensions.plus
import com.android.systemui.communal.ui.viewmodel.BaseCommunalViewModel
@@ -65,7 +66,7 @@ fun rememberGridDragDropState(
                state = gridState,
                contentListState = contentListState,
                scope = scope,
                updateDragPositionForRemove = updateDragPositionForRemove
                updateDragPositionForRemove = updateDragPositionForRemove,
            )
        }
    LaunchedEffect(state) {
@@ -90,7 +91,7 @@ internal constructor(
    private val state: LazyGridState,
    private val contentListState: ContentListState,
    private val scope: CoroutineScope,
    private val updateDragPositionForRemove: (offset: Offset) -> Boolean
    private val updateDragPositionForRemove: (offset: Offset) -> Boolean,
) {
    var draggingItemIndex by mutableStateOf<Int?>(null)
        private set
@@ -122,12 +123,12 @@ internal constructor(
        offset: Offset,
        screenWidth: Int,
        layoutDirection: LayoutDirection,
        contentOffset: Offset
        contentOffset: Offset,
    ): Boolean {
        val normalizedOffset =
            Offset(
                if (layoutDirection == LayoutDirection.Ltr) offset.x else screenWidth - offset.x,
                offset.y
                offset.y,
            )
        state.layoutInfo.visibleItemsInfo
            .filter { item -> contentListState.isItemEditable(item.index) }
@@ -248,7 +249,7 @@ fun Modifier.dragContainer(
                            offset,
                            screenWidth,
                            layoutDirection,
                            contentOffset
                            contentOffset,
                        )
                    ) {
                        viewModel.onReorderWidgetStart()
@@ -261,7 +262,7 @@ fun Modifier.dragContainer(
                onDragCancel = {
                    dragDropState.onDragInterrupted()
                    viewModel.onReorderWidgetCancel()
                }
                },
            )
        }
    )
@@ -276,7 +277,7 @@ fun LazyGridItemScope.DraggableItem(
    enabled: Boolean,
    selected: Boolean,
    modifier: Modifier = Modifier,
    content: @Composable (isDragging: Boolean) -> Unit
    content: @Composable (isDragging: Boolean) -> Unit,
) {
    if (!enabled) {
        return content(false)
@@ -286,7 +287,7 @@ fun LazyGridItemScope.DraggableItem(
    val itemAlpha: Float by
        animateFloatAsState(
            targetValue = if (dragDropState.isDraggingToRemove) 0.5f else 1f,
            label = "DraggableItemAlpha"
            label = "DraggableItemAlpha",
        )
    val direction = LocalLayoutDirection.current
    val draggingModifier =
@@ -303,12 +304,17 @@ fun LazyGridItemScope.DraggableItem(

    // Animate the highlight alpha manually as alpha modifier (and AnimatedVisibility) clips the
    // widget to bounds, which cuts off the highlight as we are drawing outside the widget bounds.
    val highlightSelected = !communalWidgetResizing() && selected
    val alpha by
        animateFloatAsState(
            targetValue =
                if ((dragging || selected) && !dragDropState.isDraggingToRemove) 1f else 0f,
                if ((dragging || highlightSelected) && !dragDropState.isDraggingToRemove) {
                    1f
                } else {
                    0f
                },
            animationSpec = spring(stiffness = Spring.StiffnessMediumLow),
            label = "Widget outline alpha"
            label = "Widget outline alpha",
        )

    Box(modifier) {