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

Commit d88f8f54 authored by Luca Zuccarini's avatar Luca Zuccarini
Browse files

Reorganize activity chip so only the chip itself animates.

In the previous setup, the whole tap target animated, causing it to
look weird and flicker in some cases.

Also drive-by fix to clip interaction ripples correctly to the chip.

Bug: 202516970
Fix: 397505143
Flag: com.android.systemui.status_bar_chips_modernization
Test: manual, see before and after in the bug
Change-Id: I7917c395612bbabc330efb9d28c707cff7eebbfe
parent 7fd9e92e
Loading
Loading
Loading
Loading
+66 −108
Original line number Diff line number Diff line
@@ -18,24 +18,18 @@ package com.android.systemui.statusbar.chips.ui.compose

import android.content.res.ColorStateList
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.layout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
@@ -43,7 +37,6 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.Expandable
import com.android.compose.modifiers.thenIf
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
@@ -60,32 +53,45 @@ fun OngoingActivityChip(
    iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
    modifier: Modifier = Modifier,
) {
    val contentDescription =
        when (val icon = model.icon) {
            is OngoingActivityChipModel.ChipIcon.StatusBarView -> icon.contentDescription.load()
            is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon ->
                icon.contentDescription.load()
            is OngoingActivityChipModel.ChipIcon.SingleColorIcon,
            null -> null
        }

    val borderStroke =
        model.colors.outline(LocalContext.current)?.let {
            BorderStroke(dimensionResource(R.dimen.ongoing_activity_chip_outline_width), Color(it))
        }

    val onClick =
        when (val clickBehavior = model.clickBehavior) {
        is OngoingActivityChipModel.ClickBehavior.ExpandAction -> {
            // Wrap the chip in an Expandable so we can animate the expand transition.
            ExpandableChip(
                color = { Color.Transparent },
                shape =
                    RoundedCornerShape(
                        dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)
                    ),
                modifier = modifier,
            ) { expandable ->
                ChipBody(model, iconViewStore, onClick = { clickBehavior.onClick(expandable) })
            is OngoingActivityChipModel.ClickBehavior.ExpandAction -> { expandable: Expandable ->
                    clickBehavior.onClick(expandable)
                }
            is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> { _ ->
                    clickBehavior.onClick()
                }
        is OngoingActivityChipModel.ClickBehavior.ShowHeadsUpNotification -> {
            ChipBody(
                model,
                iconViewStore,
                onClick = { clickBehavior.onClick() },
                modifier = modifier,
            )
            is OngoingActivityChipModel.ClickBehavior.None -> null
        }

        is OngoingActivityChipModel.ClickBehavior.None -> {
            ChipBody(model, iconViewStore, modifier = modifier)
    Expandable(
        color = Color(model.colors.background(LocalContext.current).defaultColor),
        shape =
            RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius)),
        modifier =
            modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height)).semantics {
                if (contentDescription != null) {
                    this.contentDescription = contentDescription
                }
            },
        borderStroke = borderStroke,
        onClick = onClick,
    ) {
        ChipBody(model, iconViewStore, isClickable = onClick != null)
    }
}

@@ -93,22 +99,13 @@ fun OngoingActivityChip(
private fun ChipBody(
    model: OngoingActivityChipModel.Active,
    iconViewStore: NotificationIconContainerViewBinder.IconViewStore?,
    isClickable: Boolean,
    modifier: Modifier = Modifier,
    onClick: (() -> Unit)? = null,
) {
    val context = LocalContext.current
    val isClickable = onClick != null
    val hasEmbeddedIcon =
        model.icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
            model.icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
    val contentDescription =
        when (val icon = model.icon) {
            is OngoingActivityChipModel.ChipIcon.StatusBarView -> icon.contentDescription.load()
            is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon ->
                icon.contentDescription.load()
            is OngoingActivityChipModel.ChipIcon.SingleColorIcon -> null
            null -> null
        }

    val chipSidePadding = dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
    val minWidth =
        if (isClickable) {
@@ -119,32 +116,12 @@ private fun ChipBody(
            dimensionResource(id = R.dimen.ongoing_activity_chip_min_text_width) + chipSidePadding
        }

    val outline = model.colors.outline(context)
    val outlineWidth = dimensionResource(R.dimen.ongoing_activity_chip_outline_width)

    val shape =
        RoundedCornerShape(dimensionResource(id = R.dimen.ongoing_activity_chip_corner_radius))

    // Use a Box with `fillMaxHeight` to create a larger click surface for the chip. The visible
    // height of the chip is determined by the height of the background of the Row below.
    Box(
        contentAlignment = Alignment.Center,
        modifier =
            modifier
                .fillMaxHeight()
                .clickable(enabled = isClickable, onClick = onClick ?: {})
                .semantics {
                    if (contentDescription != null) {
                        this.contentDescription = contentDescription
                    }
                },
    ) {
    Row(
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically,
        modifier =
                Modifier.height(dimensionResource(R.dimen.ongoing_appops_chip_height))
                    .thenIf(isClickable) { Modifier.widthIn(min = minWidth) }
            modifier
                .fillMaxHeight()
                .layout { measurable, constraints ->
                    val placeable = measurable.measure(constraints)
                    layout(placeable.width, placeable.height) {
@@ -153,20 +130,11 @@ private fun ChipBody(
                        }
                    }
                }
                    .background(Color(model.colors.background(context).defaultColor), shape = shape)
                    .thenIf(outline != null) {
                        Modifier.border(
                            width = outlineWidth,
                            color = Color(outline!!),
                            shape = shape,
                        )
                    }
                .padding(
                    horizontal =
                        if (hasEmbeddedIcon) {
                            dimensionResource(
                                    R.dimen
                                        .ongoing_activity_chip_side_padding_for_embedded_padding_icon
                                R.dimen.ongoing_activity_chip_side_padding_for_embedded_padding_icon
                            )
                        } else {
                            dimensionResource(id = R.dimen.ongoing_activity_chip_side_padding)
@@ -183,7 +151,6 @@ private fun ChipBody(
        }
    }
}
}

@Composable
private fun ChipIcon(
@@ -228,6 +195,7 @@ private fun StatusBarIcon(
    iconFactory: () -> StatusBarIconView?,
) {
    val context = LocalContext.current
    val colorTintList = ColorStateList.valueOf(colors.text(context))

    val iconSizePx =
        context.resources.getDimensionPixelSize(
@@ -238,18 +206,8 @@ private fun StatusBarIcon(
        factory = { _ ->
            iconFactory.invoke()?.apply {
                layoutParams = ViewGroup.LayoutParams(iconSizePx, iconSizePx)
                imageTintList = ColorStateList.valueOf(colors.text(context))
            } ?: throw IllegalStateException("Missing StatusBarIconView for $notificationKey")
        },
        update = { iconView -> iconView.imageTintList = colorTintList },
    )
}

@Composable
private fun ExpandableChip(
    color: () -> Color,
    shape: Shape,
    modifier: Modifier = Modifier,
    content: @Composable (Expandable) -> Unit,
) {
    Expandable(color = color(), shape = shape, modifier = modifier.clip(shape)) { content(it) }
}