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

Commit 889715e0 authored by Lucas Silva's avatar Lucas Silva Committed by Android (Google) Code Review
Browse files

Merge changes from topic "fix-item-frame" into main

* changes:
  Allow widgets to be resized on glanceable hub
  Address some issues with ResizeableItemFrame
  Add resize functionality to CommunalEditModeviewModel
parents fd8368f5 b8178230
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) {
+14 −11
Original line number Diff line number Diff line
@@ -30,6 +30,8 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -52,12 +54,11 @@ import com.android.systemui.communal.ui.viewmodel.ResizeableItemFrameViewModel
import com.android.systemui.lifecycle.rememberViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull

@Composable
private fun UpdateGridLayoutInfo(
    viewModel: ResizeableItemFrameViewModel,
    index: Int,
    key: String,
    gridState: LazyGridState,
    minItemSpan: Int,
    gridContentPadding: PaddingValues,
@@ -67,7 +68,7 @@ private fun UpdateGridLayoutInfo(
    LaunchedEffect(
        density,
        viewModel,
        index,
        key,
        gridState,
        minItemSpan,
        gridContentPadding,
@@ -85,9 +86,8 @@ private fun UpdateGridLayoutInfo(
                snapshotFlow { gridState.layoutInfo.maxSpan },
                snapshotFlow { gridState.layoutInfo.viewportSize.height },
                snapshotFlow {
                        gridState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }
                    }
                    .filterNotNull(),
                    gridState.layoutInfo.visibleItemsInfo.firstOrNull { it.key == key }
                },
                ::Triple,
            )
            .collectLatest { (maxItemSpan, viewportHeightPx, itemInfo) ->
@@ -97,8 +97,8 @@ private fun UpdateGridLayoutInfo(
                    viewportHeightPx,
                    maxItemSpan,
                    minItemSpan,
                    itemInfo.row,
                    itemInfo.span,
                    itemInfo?.row,
                    itemInfo?.span,
                )
            }
    }
@@ -161,7 +161,7 @@ private fun BoxScope.DragHandle(
 */
@Composable
fun ResizableItemFrame(
    index: Int,
    key: String,
    gridState: LazyGridState,
    minItemSpan: Int,
    gridContentPadding: PaddingValues,
@@ -177,6 +177,7 @@ fun ResizableItemFrame(
    content: @Composable () -> Unit,
) {
    val brush = SolidColor(outlineColor)
    val onResizeUpdated by rememberUpdatedState(onResize)
    val viewModel =
        rememberViewModel(traceName = "ResizeableItemFrame.viewModel") {
            ResizeableItemFrameViewModel()
@@ -230,13 +231,15 @@ fun ResizableItemFrame(

            UpdateGridLayoutInfo(
                viewModel,
                index,
                key,
                gridState,
                minItemSpan,
                gridContentPadding,
                verticalArrangement,
            )
            LaunchedEffect(viewModel) { viewModel.resizeInfo.collectLatest(onResize) }
            LaunchedEffect(viewModel) {
                viewModel.resizeInfo.collectLatest { info -> onResizeUpdated(info) }
            }
        }
    }
}
+17 −2
Original line number Diff line number Diff line
@@ -25,8 +25,10 @@ import android.content.applicationContext
import android.graphics.Bitmap
import android.os.UserHandle
import android.os.userManager
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags.FLAG_COMMUNAL_WIDGET_RESIZING
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.data.repository.fakePackageChangeRepository
import com.android.systemui.common.shared.model.PackageInstallSession
@@ -156,6 +158,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        appWidgetId = communalWidgetItemEntry.widgetId,
                        providerInfo = providerInfoA,
                        rank = communalItemRankEntry.rank,
                        spanY = communalWidgetItemEntry.spanY,
                    )
                )

@@ -188,11 +191,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        appWidgetId = 1,
                        providerInfo = providerInfoA,
                        rank = 1,
                        spanY = 3,
                    ),
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 2,
                        providerInfo = providerInfoB,
                        rank = 2,
                        spanY = 3,
                    ),
                )
        }
@@ -219,11 +224,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        appWidgetId = 1,
                        providerInfo = providerInfoA,
                        rank = 1,
                        spanY = 3,
                    ),
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 2,
                        providerInfo = providerInfoB,
                        rank = 2,
                        spanY = 3,
                    ),
                )

@@ -238,11 +245,13 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        // Verify that provider info updated
                        providerInfo = providerInfoC,
                        rank = 1,
                        spanY = 3,
                    ),
                    CommunalWidgetContentModel.Available(
                        appWidgetId = 2,
                        providerInfo = providerInfoB,
                        rank = 2,
                        spanY = 3,
                    ),
                )
        }
@@ -681,6 +690,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        appWidgetId = 1,
                        providerInfo = providerInfoA,
                        rank = 1,
                        spanY = 3,
                    ),
                    CommunalWidgetContentModel.Pending(
                        appWidgetId = 2,
@@ -688,6 +698,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        componentName = ComponentName("pk_2", "cls_2"),
                        icon = fakeIcon,
                        user = mainUser,
                        spanY = 3,
                    ),
                )
        }
@@ -723,6 +734,7 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        componentName = ComponentName("pk_1", "cls_1"),
                        icon = fakeIcon,
                        user = mainUser,
                        spanY = 3,
                    )
                )

@@ -740,20 +752,23 @@ class CommunalWidgetRepositoryImplTest : SysuiTestCase() {
                        appWidgetId = 1,
                        providerInfo = providerInfoA,
                        rank = 1,
                        spanY = 3,
                    )
                )
        }

    @Test
    @EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
    fun updateWidgetSpanY_updatesWidgetInDaoAndRequestsBackup() =
        testScope.runTest {
            val widgetId = 1
            val newSpanY = 6
            val widgetIdToRankMap = emptyMap<Int, Int>()

            underTest.updateWidgetSpanY(widgetId, newSpanY)
            underTest.resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
            runCurrent()

            verify(communalWidgetDao).updateWidgetSpanY(widgetId, newSpanY)
            verify(communalWidgetDao).resizeWidget(widgetId, newSpanY, widgetIdToRankMap)
            verify(backupManager).dataChanged()
        }

Loading