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

Commit 4e15a5a4 authored by Anton Potapov's avatar Anton Potapov
Browse files

Update Volume Panel visuals for new redesign.

Flag: com.android.systemui.volume_redesign
Test: atest VolumePanelScreenshotTest
Bug: 369994101
Change-Id: I185d6c80407c7a90f115dac3bcc1162fad2d4524
parent bdbd1f52
Loading
Loading
Loading
Loading
+17 −4
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.Expandable
import com.android.systemui.Flags
import com.android.systemui.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
@@ -56,7 +57,7 @@ import kotlinx.coroutines.flow.StateFlow
/** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */
class ButtonComponent(
    private val viewModelFlow: StateFlow<ButtonViewModel?>,
    private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit
    private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit,
) : ComposeVolumePanelUiComponent {

    @Composable
@@ -84,14 +85,26 @@ class ButtonComponent(
                        },
                    color =
                        if (viewModel.isActive) {
                            if (Flags.volumeRedesign()) {
                                MaterialTheme.colorScheme.primary
                            } else {
                                MaterialTheme.colorScheme.tertiaryContainer
                            }
                        } else {
                            if (Flags.volumeRedesign()) {
                                MaterialTheme.colorScheme.surfaceContainerHigh
                            } else {
                                MaterialTheme.colorScheme.surface
                            }
                        },
                    shape = RoundedCornerShape(20.dp),
                    contentColor =
                        if (viewModel.isActive) {
                            if (Flags.volumeRedesign()) {
                                MaterialTheme.colorScheme.onPrimary
                            } else {
                                MaterialTheme.colorScheme.onTertiaryContainer
                            }
                        } else {
                            MaterialTheme.colorScheme.onSurface
                        },
+25 −10
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.Flags
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel
import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
@@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.StateFlow
/** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */
class ToggleButtonComponent(
    private val viewModelFlow: StateFlow<ButtonViewModel?>,
    private val onCheckedChange: (isChecked: Boolean) -> Unit
    private val onCheckedChange: (isChecked: Boolean) -> Unit,
) : ComposeVolumePanelUiComponent {

    @Composable
@@ -68,16 +69,30 @@ class ToggleButtonComponent(
            BottomComponentButtonSurface {
                val colors =
                    if (viewModel.isActive) {
                        if (Flags.volumeRedesign()) {
                            ButtonDefaults.buttonColors(
                                containerColor = MaterialTheme.colorScheme.primary,
                                contentColor = MaterialTheme.colorScheme.onPrimary,
                            )
                        } else {
                            ButtonDefaults.buttonColors(
                                containerColor = MaterialTheme.colorScheme.tertiaryContainer,
                                contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
                            )
                        }
                    } else {
                        if (Flags.volumeRedesign()) {
                            ButtonDefaults.buttonColors(
                                containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
                                contentColor = MaterialTheme.colorScheme.onSurface,
                            )
                        } else {
                            ButtonDefaults.buttonColors(
                                containerColor = Color.Transparent,
                                contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
                            )
                        }
                    }
                Button(
                    modifier =
                        Modifier.fillMaxSize().padding(8.dp).semantics {
@@ -93,7 +108,7 @@ class ToggleButtonComponent(
                    onClick = { onCheckedChange(!viewModel.isActive) },
                    shape = RoundedCornerShape(20.dp),
                    colors = colors,
                    contentPadding = PaddingValues(0.dp)
                    contentPadding = PaddingValues(0.dp),
                ) {
                    Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
                }
+74 −9
Original line number Diff line number Diff line
@@ -37,11 +37,13 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
@@ -51,8 +53,11 @@ import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.PlatformIconButton
import com.android.compose.PlatformSliderColors
import com.android.compose.modifiers.padding
import com.android.compose.modifiers.thenIf
import com.android.systemui.Flags
import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel

@@ -84,7 +89,11 @@ fun ColumnVolumeSliders(
            val sliderPadding by topSliderPadding(isExpandable)

            VolumeSlider(
                modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(),
                modifier =
                    Modifier.thenIf(!Flags.volumeRedesign()) {
                            Modifier.padding(end = { sliderPadding.roundToPx() })
                        }
                        .fillMaxWidth(),
                state = sliderState,
                onValueChange = { newValue: Float ->
                    sliderViewModel.onValueChanged(sliderState, newValue)
@@ -93,9 +102,22 @@ fun ColumnVolumeSliders(
                onIconTapped = { sliderViewModel.toggleMuted(sliderState) },
                sliderColors = sliderColors,
                hapticsViewModelFactory = sliderViewModel.getSliderHapticsViewModelFactory(),
                button =
                    if (Flags.volumeRedesign()) {
                        {
                            ExpandButton(
                                isExpanded = isExpanded,
                                isExpandable = isExpandable,
                                onExpandedChanged = onExpandedChanged,
                            )
                        }
                    } else {
                        null
                    },
            )

            ExpandButton(
            if (!Flags.volumeRedesign()) {
                ExpandButtonLegacy(
                    modifier = Modifier.align(Alignment.CenterEnd),
                    isExpanded = isExpanded,
                    isExpandable = isExpandable,
@@ -103,6 +125,7 @@ fun ColumnVolumeSliders(
                    sliderColors = sliderColors,
                )
            }
        }
        AnimatedVisibility(
            visible = isExpanded || !isExpandable,
            label = "CollapsableSliders",
@@ -153,7 +176,7 @@ fun ColumnVolumeSliders(
}

@Composable
private fun ExpandButton(
private fun ExpandButtonLegacy(
    isExpanded: Boolean,
    isExpandable: Boolean,
    onExpandedChanged: (Boolean) -> Unit,
@@ -200,6 +223,48 @@ private fun ExpandButton(
    }
}

@Composable
private fun ExpandButton(
    isExpanded: Boolean,
    isExpandable: Boolean,
    onExpandedChanged: (Boolean) -> Unit,
    modifier: Modifier = Modifier,
) {
    val expandButtonStateDescription =
        if (isExpanded) {
            stringResource(R.string.volume_panel_expanded_sliders)
        } else {
            stringResource(R.string.volume_panel_collapsed_sliders)
        }
    AnimatedVisibility(
        modifier = modifier,
        visible = isExpandable,
        enter = expandButtonEnterTransition(),
        exit = expandButtonExitTransition(),
    ) {
        PlatformIconButton(
            modifier =
                Modifier.size(width = 48.dp, height = 40.dp).semantics {
                    role = Role.Switch
                    stateDescription = expandButtonStateDescription
                },
            onClick = { onExpandedChanged(!isExpanded) },
            colors =
                IconButtonDefaults.iconButtonColors(
                    containerColor = Color.Transparent,
                    contentColor = MaterialTheme.colorScheme.onSurfaceVariant,
                ),
            iconResource =
                if (isExpanded) {
                    R.drawable.ic_arrow_down_24dp
                } else {
                    R.drawable.ic_arrow_up_24dp
                },
            contentDescription = null,
        )
    }
}

private fun enterTransition(index: Int, totalCount: Int): EnterTransition {
    val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0)
    val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100)
+105 −2
Original line number Diff line number Diff line
@@ -24,9 +24,18 @@ import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.getValue
@@ -48,6 +57,7 @@ import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformSlider
import com.android.compose.PlatformSliderColors
import com.android.systemui.Flags
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.compose.modifiers.sysuiResTag
@@ -61,11 +71,104 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl
fun VolumeSlider(
    state: SliderState,
    onValueChange: (newValue: Float) -> Unit,
    onValueChangeFinished: (() -> Unit)? = null,
    onIconTapped: () -> Unit,
    sliderColors: PlatformSliderColors,
    modifier: Modifier = Modifier,
    hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
    onValueChangeFinished: (() -> Unit)? = null,
    button: (@Composable () -> Unit)? = null,
) {
    if (!Flags.volumeRedesign()) {
        LegacyVolumeSlider(
            state = state,
            onValueChange = onValueChange,
            onIconTapped = onIconTapped,
            sliderColors = sliderColors,
            onValueChangeFinished = onValueChangeFinished,
            modifier = modifier,
            hapticsViewModelFactory = hapticsViewModelFactory,
        )
        return
    }

    val value by valueState(state)
    Column(modifier) {
        Row(
            horizontalArrangement = Arrangement.spacedBy(12.dp),
            modifier = Modifier.fillMaxWidth(),
        ) {
            state.icon?.let {
                Icon(
                    icon = it,
                    tint = MaterialTheme.colorScheme.onSurface,
                    modifier = Modifier.size(40.dp).padding(8.dp),
                )
            }
            Text(
                text = state.label,
                style = MaterialTheme.typography.titleMedium,
                color = MaterialTheme.colorScheme.onSurface,
                modifier = Modifier.weight(1f).align(Alignment.CenterVertically),
            )
            button?.invoke()
        }
        Slider(
            value = value,
            valueRange = state.valueRange,
            onValueChange = onValueChange,
            onValueChangeFinished = onValueChangeFinished,
            enabled = state.isEnabled,
            modifier =
                Modifier.height(40.dp).sysuiResTag(state.label).clearAndSetSemantics {
                    if (state.isEnabled) {
                        contentDescription = state.label
                        state.a11yClickDescription?.let {
                            customActions =
                                listOf(
                                    CustomAccessibilityAction(it) {
                                        onIconTapped()
                                        true
                                    }
                                )
                        }

                        state.a11yStateDescription?.let { stateDescription = it }
                        progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
                    } else {
                        disabled()
                        contentDescription =
                            state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
                    }
                    setProgress { targetValue ->
                        val targetDirection =
                            when {
                                targetValue > value -> 1
                                targetValue < value -> -1
                                else -> 0
                            }

                        val newValue =
                            (value + targetDirection * state.a11yStep).coerceIn(
                                state.valueRange.start,
                                state.valueRange.endInclusive,
                            )
                        onValueChange(newValue)
                        true
                    }
                },
        )
    }
}

@Composable
private fun LegacyVolumeSlider(
    state: SliderState,
    onValueChange: (newValue: Float) -> Unit,
    onIconTapped: () -> Unit,
    sliderColors: PlatformSliderColors,
    hapticsViewModelFactory: SliderHapticsViewModel.Factory?,
    modifier: Modifier = Modifier,
    onValueChangeFinished: (() -> Unit)? = null,
) {
    val value by valueState(state)
    val interactionSource = remember { MutableInteractionSource() }
@@ -178,7 +281,7 @@ private fun valueState(state: SliderState): State<Float> {
    val shouldSkipAnimation =
        prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled
    val value =
        if (shouldSkipAnimation) mutableFloatStateOf(state.value)
        if (shouldSkipAnimation) remember { mutableFloatStateOf(state.value) }
        else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation")
    prevState = state
    return value
+3 −3
Original line number Diff line number Diff line
@@ -53,7 +53,7 @@ private enum class VolumeSliderContentComponent {
    DisabledMessage,
}

/** Shows label of the [VolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
/** Shows label of the [LegacyVolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */
@Composable
fun VolumeSliderContent(
    label: String,
@@ -89,7 +89,7 @@ fun VolumeSliderContent(
                }
            }
        },
        measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled)
        measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled),
    )
}

@@ -102,7 +102,7 @@ private class VolumeSliderContentMeasurePolicy(private val isEnabled: Boolean) :

    override fun MeasureScope.measure(
        measurables: List<Measurable>,
        constraints: Constraints
        constraints: Constraints,
    ): MeasureResult {
        val labelPlaceable =
            measurables
Loading