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

Commit bfbd7ae9 authored by Ale Nijamkin's avatar Ale Nijamkin Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Avoid extra recomposition in pattern bouncer." into main

parents 97e29daa 0b1cd1eb
Loading
Loading
Loading
Loading
+59 −54
Original line number Diff line number Diff line
@@ -39,11 +39,11 @@ import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.StrokeCap
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.layout.LayoutCoordinates
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.modifiers.thenIf
@@ -79,12 +79,6 @@ internal fun PatternBouncer(
    val lineColor = MaterialTheme.colorScheme.primary
    val lineStrokeWidth = with(LocalDensity.current) { LINE_STROKE_WIDTH_DP.dp.toPx() }

    var containerSize: IntSize by remember { mutableStateOf(IntSize(0, 0)) }
    val horizontalSpacing = containerSize.width / colCount
    val verticalSpacing = containerSize.height / rowCount
    val spacing = min(horizontalSpacing, verticalSpacing).toFloat()
    val verticalOffset = containerSize.height - spacing * rowCount

    // All dots that should be rendered on the grid.
    val dots: List<PatternDotViewModel> by viewModel.dots.collectAsState()
    // The most recently selected dot, if the user is currently dragging.
@@ -195,13 +189,14 @@ internal fun PatternBouncer(

    // This is the position of the input pointer.
    var inputPosition: Offset? by remember { mutableStateOf(null) }
    var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }

    Canvas(
        modifier
            // Need to clip to bounds to make sure that the lines don't follow the input pointer
            // when it leaves the bounds of the dot grid.
            .clipToBounds()
            .onSizeChanged { containerSize = it }
            .onGloballyPositioned { coordinates -> gridCoordinates = coordinates }
            .thenIf(isInputEnabled) {
                Modifier.pointerInput(Unit) {
                        awaitEachGesture {
@@ -232,19 +227,26 @@ internal fun PatternBouncer(
                            viewModel.onDrag(
                                xPx = change.position.x,
                                yPx = change.position.y,
                                containerSizePx = containerSize.width,
                                verticalOffsetPx = verticalOffset,
                                containerSizePx = size.width,
                            )
                        }
                    }
            }
    ) {
        gridCoordinates?.let { nonNullCoordinates ->
            val containerSize = nonNullCoordinates.size
            val horizontalSpacing = containerSize.width.toFloat() / colCount
            val verticalSpacing = containerSize.height.toFloat() / rowCount
            val spacing = min(horizontalSpacing, verticalSpacing)
            val verticalOffset = containerSize.height - spacing * rowCount

            if (isAnimationEnabled) {
                // Draw lines between dots.
                selectedDots.forEachIndexed { index, dot ->
                    if (index > 0) {
                        val previousDot = selectedDots[index - 1]
                    val lineFadeOutAnimationProgress = lineFadeOutAnimatables[previousDot]!!.value
                        val lineFadeOutAnimationProgress =
                            lineFadeOutAnimatables[previousDot]!!.value
                        val startLerp = 1 - lineFadeOutAnimationProgress
                        val from = pixelOffset(previousDot, spacing, verticalOffset)
                        val to = pixelOffset(dot, spacing, verticalOffset)
@@ -264,11 +266,13 @@ internal fun PatternBouncer(
                    }
                }

            // Draw the line between the most recently-selected dot and the input pointer position.
                // Draw the line between the most recently-selected dot and the input pointer
                // position.
                inputPosition?.let { lineEnd ->
                    currentDot?.let { dot ->
                        val from = pixelOffset(dot, spacing, verticalOffset)
                    val lineLength = sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
                        val lineLength =
                            sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
                        drawLine(
                            start = from,
                            end = lineEnd,
@@ -291,6 +295,7 @@ internal fun PatternBouncer(
            }
        }
    }
}

/** Returns an [Offset] representation of the given [dot], in pixel coordinates. */
private fun pixelOffset(
+4 −5
Original line number Diff line number Diff line
@@ -94,24 +94,23 @@ class PatternBouncerViewModel(
     * @param yPx The vertical coordinate of the position of the user's pointer, in pixels.
     * @param containerSizePx The size of the container of the dot grid, in pixels. It's assumed
     *   that the dot grid is perfectly square such that width and height are equal.
     * @param verticalOffsetPx How far down from `0` does the dot grid start on the display.
     */
    fun onDrag(xPx: Float, yPx: Float, containerSizePx: Int, verticalOffsetPx: Float) {
    fun onDrag(xPx: Float, yPx: Float, containerSizePx: Int) {
        val cellWidthPx = containerSizePx / columnCount
        val cellHeightPx = containerSizePx / rowCount

        if (xPx < 0 || yPx < verticalOffsetPx) {
        if (xPx < 0 || yPx < 0) {
            return
        }

        val dotColumn = (xPx / cellWidthPx).toInt()
        val dotRow = ((yPx - verticalOffsetPx) / cellHeightPx).toInt()
        val dotRow = (yPx / cellHeightPx).toInt()
        if (dotColumn > columnCount - 1 || dotRow > rowCount - 1) {
            return
        }

        val dotPixelX = dotColumn * cellWidthPx + cellWidthPx / 2
        val dotPixelY = dotRow * cellHeightPx + cellHeightPx / 2 + verticalOffsetPx
        val dotPixelY = dotRow * cellHeightPx + cellHeightPx / 2

        val distance = sqrt((xPx - dotPixelX).pow(2) + (yPx - dotPixelY).pow(2))
        val hitRadius = hitFactor * min(cellWidthPx, cellHeightPx) / 2
+0 −2
Original line number Diff line number Diff line
@@ -322,7 +322,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
                            xPx = 30f * coordinate.x + 15,
                            yPx = 30f * coordinate.y + 15,
                            containerSizePx = 90,
                            verticalOffsetPx = 0f,
                        )
                    }

@@ -369,7 +368,6 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
            xPx = dotSize * coordinate.x + 15f,
            yPx = dotSize * coordinate.y + 15f,
            containerSizePx = containerSize,
            verticalOffsetPx = 0f,
        )
    }