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

Commit a01ff0ff authored by Evan Laird's avatar Evan Laird Committed by Android (Google) Code Review
Browse files

Merge "[battery] implement RTL naively" into main

parents 010976d8 f54508b4
Loading
Loading
Loading
Loading
+71 −19
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
@@ -42,9 +43,12 @@ import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.MeasurePolicy
import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.MeasureScope
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.layout.onLayoutRectChanged
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastFirst
import androidx.compose.ui.util.fastFirstOrNull
import com.android.systemui.common.ui.compose.load
@@ -301,27 +305,72 @@ class BatteryMeasurePolicy : MeasurePolicy {
        }
        val totalHeight = batterySize.height.roundToInt()
        return layout(totalWidth, totalHeight) {
            batteryFramePlaceable.place(0, 0)
            if (layoutDirection == LayoutDirection.Rtl) {
                val (offsetX, placeable) =
                    when {
                        // Attr overlaps the battery frame by 20% of its own width
                        attrPlaceable != null ->
                            (attrPlaceable.width * (1 - attrOverlap)).roundToInt() to attrPlaceable

                        // Cap has exactly 1dp (scaled) of space after it
                        capPlaceable != null ->
                            capPlaceable.width + scale.roundToInt() to capPlaceable

                        else -> 0 to null
                    }

                // Place the battery frame first so the layers are in the right order
                batteryFramePlaceable.place(offsetX, 0)

            attrPlaceable?.apply {
                // Overlap the attribution by 20% of its width
                val xOffset = batteryFramePlaceable.width - (0.2 * width).roundToInt()
                val yOffset =
                    ((batteryFramePlaceable.height - attrPlaceable.height) / 2f).roundToInt()
                place(xOffset, yOffset)
                // Then place the cap or attribution. In RTL, it always is left-aligned
                placeable?.apply {
                    placeCenteredVertically(
                        placeable = this,
                        containerHeight = batteryFramePlaceable.height,
                        xOffset = 0,
                    )
                }
            } else {
                batteryFramePlaceable.place(0, 0)

            capPlaceable?.apply {
                // Cap is offset by exactly 1dp (scaled)
                val xOffset = batteryFramePlaceable.width + scale.roundToInt()
                val yOffset =
                    ((batteryFramePlaceable.height - capPlaceable.height) / 2f).roundToInt()
                place(xOffset, yOffset)
                val (xOffset, placeable) =
                    when {
                        attrPlaceable != null ->
                            (batteryFramePlaceable.width - (attrOverlap * attrPlaceable.width))
                                .roundToInt() to attrPlaceable
                        capPlaceable != null ->
                            (batteryFramePlaceable.width + scale.roundToInt()) to capPlaceable
                        else -> 0 to null
                    }

                placeable?.apply {
                    placeCenteredVertically(
                        placeable = this,
                        containerHeight = batteryFramePlaceable.height,
                        xOffset = xOffset,
                    )
                }
            }
        }
    }

    private fun Placeable.PlacementScope.placeCenteredVertically(
        placeable: Placeable,
        containerHeight: Int,
        xOffset: Int,
    ) {
        placeable.place(x = xOffset, y = placeable.centerYOffset(containerHeight))
    }

    private fun Placeable.centerYOffset(outerHeight: Int) =
        ((outerHeight - height) / 2f).roundToInt()

    companion object {
        // Overlap the attribution by 20%
        private const val attrOverlap = 0.2f
    }
}

/**
 * Draws just the round-rect piece of the battery frame. If [glyphsProvider] is non-empty, then this
 * composable also renders the glyphs centered in the frame.
@@ -339,6 +388,7 @@ fun BatteryBody(
    contentDescription: String = "",
) {
    Canvas(modifier = modifier, contentDescription = contentDescription) {
        val rtl = layoutDirection == LayoutDirection.Rtl
        val level = levelProvider()
        val colors = colorsProvider()

@@ -373,9 +423,9 @@ fun BatteryBody(
                // 3. clip the fill to the level if we have it
                if (level != null && level > 0) {
                    clipRect(
                        left = 0f,
                        left = if (!rtl) 0f else BatteryFrame.innerWidth - level.scaledLevel(),
                        top = 0f,
                        right = level.scaledLevel(),
                        right = if (!rtl) level.scaledLevel() else BatteryFrame.innerWidth,
                        bottom = BatteryFrame.innerHeight,
                    ) {
                        // 4. Draw the rounded rect fill fully, it'll be clipped above
@@ -418,11 +468,12 @@ fun BatteryCap(
    modifier: Modifier = Modifier,
) {
    val pathSpec = BatteryFrame.capPathSpec
    Canvas(modifier = modifier) {
    val rtl = LocalLayoutDirection.current == LayoutDirection.Rtl
    Canvas(modifier = modifier.scale(scaleX = if (rtl) -1f else 1f, scaleY = 1f)) {
        val colors = colorsProvider()
        val isFull = isFullProvider()
        val s = pathSpec.scaleTo(size.width, size.height)
        scale(scale = s, pivot = Offset.Zero) {
        scale(s, pivot = Offset.Zero) {
            val color = if (isFull) colors.fill else colors.backgroundOnly
            drawPath(pathSpec.path, color = color)
        }
@@ -436,6 +487,7 @@ fun BatteryAttribution(
    modifier: Modifier = Modifier,
) {
    val stroke = remember { Stroke(width = 2f) }
    // Do not RTL the attribution, because they are text-like. '?' shouldn't be flipped, for example
    Canvas(modifier = modifier) {
        val s = attr.scaleTo(size.width, size.height)
        val colors = colorsProvider()