Loading packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,20 @@ class DragAndDropStateTest : SysuiTestCase() { TestEditTiles.forEach { assertThat(underTest.isMoving(it.tileSpec)).isFalse() } } @Test fun onMoveOutOfBounds_removeMovingTileFromCurrentList() { val movingTileSpec = TestEditTiles[0].tileSpec // Start the drag movement underTest.onStarted(movingTileSpec) // Move the tile outside of the list underTest.movedOutOfBounds() // Asserts the moving tile is not current assertThat(listState.tiles.first { it.tileSpec == movingTileSpec }.isCurrent).isFalse() } companion object { private fun createEditTile(tileSpec: String): EditTileViewModel { return EditTileViewModel( Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt +49 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ class DragAndDropState( val sourceSpec: MutableState<TileSpec?>, private val listState: EditTileListState ) { val dragInProgress: Boolean get() = sourceSpec.value != null /** Returns index of the dragged tile if it's present in the list. Returns -1 if not. */ fun currentPosition(): Int { return sourceSpec.value?.let { listState.indexOf(it) } ?: -1 Loading @@ -65,6 +68,12 @@ class DragAndDropState( sourceSpec.value?.let { listState.move(it, targetSpec) } } fun movedOutOfBounds() { // Removing the tiles from the current tile grid if it moves out of bounds. This clears // the spacer and makes it apparent that dropping the tile at that point would remove it. sourceSpec.value?.let { listState.removeFromCurrent(it) } } fun onDrop() { sourceSpec.value = null } Loading Loading @@ -111,6 +120,42 @@ fun Modifier.dragAndDropTile( ) } /** * Registers a composable as a [DragAndDropTarget] to receive drop events. Use this outside the tile * grid to catch out of bounds drops. * * @param dragAndDropState The [DragAndDropState] using the tiles list * @param onDrop Action to be executed when a [TileSpec] is dropped on the composable */ @Composable fun Modifier.dragAndDropRemoveZone( dragAndDropState: DragAndDropState, onDrop: (TileSpec) -> Unit, ): Modifier { val target = remember(dragAndDropState) { object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { return dragAndDropState.sourceSpec.value?.let { onDrop(it) dragAndDropState.onDrop() true } ?: false } override fun onEntered(event: DragAndDropEvent) { dragAndDropState.movedOutOfBounds() } } } return dragAndDropTarget( shouldStartDragAndDrop = { event -> event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) }, target = target, ) } /** * Registers a tile list as a [DragAndDropTarget] to receive drop events. Use this on list * containers to catch drops outside of tiles. Loading @@ -128,6 +173,10 @@ fun Modifier.dragAndDropTileList( val target = remember(dragAndDropState) { object : DragAndDropTarget { override fun onEnded(event: DragAndDropEvent) { dragAndDropState.onDrop() } override fun onDrop(event: DragAndDropEvent): Boolean { return dragAndDropState.sourceSpec.value?.let { onDrop(it, dragAndDropState.currentPosition()) Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +12 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,18 @@ class EditTileListState(tiles: List<EditTileViewModel>) { tiles.apply { add(toIndex, removeAt(fromIndex).copy(isCurrent = isMovingToCurrent)) } } /** * Sets the [TileSpec] as a non-current tile. Use this when a tile is dragged out of the current * tile grid. */ fun removeFromCurrent(tileSpec: TileSpec) { val fromIndex = indexOf(tileSpec) if (fromIndex >= 0 && fromIndex < tiles.size) { // Mark the moving tile as non-current tiles[fromIndex] = tiles[fromIndex].copy(isCurrent = false) } } fun indexOf(tileSpec: TileSpec): Int { return tiles.indexOfFirst { it.tileSpec == tileSpec } } Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ constructor( DefaultEditTileGrid( tiles = tiles, isIconOnly = isIcon, columns = GridCells.Fixed(columns), columns = columns, modifier = modifier, onAddTile = onAddTile, onRemoveTile = onRemoveTile, Loading packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt +5 −10 Original line number Diff line number Diff line Loading @@ -305,9 +305,9 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition largeTiles, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, acceptDrops = { true }, onDrop = onDrop, ) Loading @@ -318,10 +318,10 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition smallTiles, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, showLabels = showLabels, dragAndDropState = dragAndDropState, acceptDrops = { true }, onDrop = onDrop, ) Loading @@ -332,10 +332,10 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition tilesCustom, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, showLabels = showLabels, dragAndDropState = dragAndDropState, acceptDrops = { true }, onDrop = onDrop, ) Loading Loading @@ -372,11 +372,6 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition } } private fun gridHeight(nTiles: Int, tileHeight: Dp, columns: Int, padding: Dp): Dp { val rows = (nTiles + columns - 1) / columns return ((tileHeight + padding) * rows) - padding } /** Fill up the rest of the row if it's not complete. */ private fun LazyGridScope.fillUpRow(nTiles: Int, columns: Int) { if (nTiles % columns != 0) { Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropStateTest.kt +14 −0 Original line number Diff line number Diff line Loading @@ -82,6 +82,20 @@ class DragAndDropStateTest : SysuiTestCase() { TestEditTiles.forEach { assertThat(underTest.isMoving(it.tileSpec)).isFalse() } } @Test fun onMoveOutOfBounds_removeMovingTileFromCurrentList() { val movingTileSpec = TestEditTiles[0].tileSpec // Start the drag movement underTest.onStarted(movingTileSpec) // Move the tile outside of the list underTest.movedOutOfBounds() // Asserts the moving tile is not current assertThat(listState.tiles.first { it.tileSpec == movingTileSpec }.isCurrent).isFalse() } companion object { private fun createEditTile(tileSpec: String): EditTileViewModel { return EditTileViewModel( Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt +49 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,9 @@ class DragAndDropState( val sourceSpec: MutableState<TileSpec?>, private val listState: EditTileListState ) { val dragInProgress: Boolean get() = sourceSpec.value != null /** Returns index of the dragged tile if it's present in the list. Returns -1 if not. */ fun currentPosition(): Int { return sourceSpec.value?.let { listState.indexOf(it) } ?: -1 Loading @@ -65,6 +68,12 @@ class DragAndDropState( sourceSpec.value?.let { listState.move(it, targetSpec) } } fun movedOutOfBounds() { // Removing the tiles from the current tile grid if it moves out of bounds. This clears // the spacer and makes it apparent that dropping the tile at that point would remove it. sourceSpec.value?.let { listState.removeFromCurrent(it) } } fun onDrop() { sourceSpec.value = null } Loading Loading @@ -111,6 +120,42 @@ fun Modifier.dragAndDropTile( ) } /** * Registers a composable as a [DragAndDropTarget] to receive drop events. Use this outside the tile * grid to catch out of bounds drops. * * @param dragAndDropState The [DragAndDropState] using the tiles list * @param onDrop Action to be executed when a [TileSpec] is dropped on the composable */ @Composable fun Modifier.dragAndDropRemoveZone( dragAndDropState: DragAndDropState, onDrop: (TileSpec) -> Unit, ): Modifier { val target = remember(dragAndDropState) { object : DragAndDropTarget { override fun onDrop(event: DragAndDropEvent): Boolean { return dragAndDropState.sourceSpec.value?.let { onDrop(it) dragAndDropState.onDrop() true } ?: false } override fun onEntered(event: DragAndDropEvent) { dragAndDropState.movedOutOfBounds() } } } return dragAndDropTarget( shouldStartDragAndDrop = { event -> event.mimeTypes().contains(QsDragAndDrop.TILESPEC_MIME_TYPE) }, target = target, ) } /** * Registers a tile list as a [DragAndDropTarget] to receive drop events. Use this on list * containers to catch drops outside of tiles. Loading @@ -128,6 +173,10 @@ fun Modifier.dragAndDropTileList( val target = remember(dragAndDropState) { object : DragAndDropTarget { override fun onEnded(event: DragAndDropEvent) { dragAndDropState.onDrop() } override fun onDrop(event: DragAndDropEvent): Boolean { return dragAndDropState.sourceSpec.value?.let { onDrop(it, dragAndDropState.currentPosition()) Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +12 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,18 @@ class EditTileListState(tiles: List<EditTileViewModel>) { tiles.apply { add(toIndex, removeAt(fromIndex).copy(isCurrent = isMovingToCurrent)) } } /** * Sets the [TileSpec] as a non-current tile. Use this when a tile is dragged out of the current * tile grid. */ fun removeFromCurrent(tileSpec: TileSpec) { val fromIndex = indexOf(tileSpec) if (fromIndex >= 0 && fromIndex < tiles.size) { // Mark the moving tile as non-current tiles[fromIndex] = tiles[fromIndex].copy(isCurrent = false) } } fun indexOf(tileSpec: TileSpec): Int { return tiles.indexOfFirst { it.tileSpec == tileSpec } } Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/InfiniteGridLayout.kt +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ constructor( DefaultEditTileGrid( tiles = tiles, isIconOnly = isIcon, columns = GridCells.Fixed(columns), columns = columns, modifier = modifier, onAddTile = onAddTile, onRemoveTile = onRemoveTile, Loading
packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/PartitionedGridLayout.kt +5 −10 Original line number Diff line number Diff line Loading @@ -305,9 +305,9 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition largeTiles, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, acceptDrops = { true }, onDrop = onDrop, ) Loading @@ -318,10 +318,10 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition smallTiles, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, showLabels = showLabels, dragAndDropState = dragAndDropState, acceptDrops = { true }, onDrop = onDrop, ) Loading @@ -332,10 +332,10 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition tilesCustom, ClickAction.ADD, addTileToEnd, onDoubleTap, isIconOnly, dragAndDropState, onDoubleTap = onDoubleTap, showLabels = showLabels, dragAndDropState = dragAndDropState, acceptDrops = { true }, onDrop = onDrop, ) Loading Loading @@ -372,11 +372,6 @@ class PartitionedGridLayout @Inject constructor(private val viewModel: Partition } } private fun gridHeight(nTiles: Int, tileHeight: Dp, columns: Int, padding: Dp): Dp { val rows = (nTiles + columns - 1) / columns return ((tileHeight + padding) * rows) - padding } /** Fill up the rest of the row if it's not complete. */ private fun LazyGridScope.fillUpRow(nTiles: Int, columns: Int) { if (nTiles % columns != 0) { Loading