Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt +17 −4 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable import com.android.compose.animation.Expandable import com.android.systemui.Flags import com.android.systemui.animation.Expandable import com.android.systemui.animation.Expandable import com.android.systemui.common.ui.compose.Icon 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.component.button.ui.viewmodel.ButtonViewModel Loading @@ -56,7 +57,7 @@ import kotlinx.coroutines.flow.StateFlow /** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */ /** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */ class ButtonComponent( class ButtonComponent( private val viewModelFlow: StateFlow<ButtonViewModel?>, private val viewModelFlow: StateFlow<ButtonViewModel?>, private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit, ) : ComposeVolumePanelUiComponent { ) : ComposeVolumePanelUiComponent { @Composable @Composable Loading Loading @@ -84,14 +85,26 @@ class ButtonComponent( }, }, color = color = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.primary } else { MaterialTheme.colorScheme.tertiaryContainer MaterialTheme.colorScheme.tertiaryContainer } } else { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.surfaceContainerHigh } else { } else { MaterialTheme.colorScheme.surface MaterialTheme.colorScheme.surface } }, }, shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp), contentColor = contentColor = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.onPrimary } else { MaterialTheme.colorScheme.onTertiaryContainer MaterialTheme.colorScheme.onTertiaryContainer } } else { } else { MaterialTheme.colorScheme.onSurface MaterialTheme.colorScheme.onSurface }, }, Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt +25 −10 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.ui.semantics.toggleableState import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.Flags import com.android.systemui.common.ui.compose.Icon 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.component.button.ui.viewmodel.ButtonViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent Loading @@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.StateFlow /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */ /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */ class ToggleButtonComponent( class ToggleButtonComponent( private val viewModelFlow: StateFlow<ButtonViewModel?>, private val viewModelFlow: StateFlow<ButtonViewModel?>, private val onCheckedChange: (isChecked: Boolean) -> Unit private val onCheckedChange: (isChecked: Boolean) -> Unit, ) : ComposeVolumePanelUiComponent { ) : ComposeVolumePanelUiComponent { @Composable @Composable Loading @@ -68,16 +69,30 @@ class ToggleButtonComponent( BottomComponentButtonSurface { BottomComponentButtonSurface { val colors = val colors = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary, ) } else { ButtonDefaults.buttonColors( ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.tertiaryContainer, containerColor = MaterialTheme.colorScheme.tertiaryContainer, contentColor = MaterialTheme.colorScheme.onTertiaryContainer, contentColor = MaterialTheme.colorScheme.onTertiaryContainer, ) ) } } else { if (Flags.volumeRedesign()) { ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, contentColor = MaterialTheme.colorScheme.onSurface, ) } else { } else { ButtonDefaults.buttonColors( ButtonDefaults.buttonColors( containerColor = Color.Transparent, containerColor = Color.Transparent, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, ) ) } } } Button( Button( modifier = modifier = Modifier.fillMaxSize().padding(8.dp).semantics { Modifier.fillMaxSize().padding(8.dp).semantics { Loading @@ -93,7 +108,7 @@ class ToggleButtonComponent( onClick = { onCheckedChange(!viewModel.isActive) }, onClick = { onCheckedChange(!viewModel.isActive) }, shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp), colors = colors, colors = colors, contentPadding = PaddingValues(0.dp) contentPadding = PaddingValues(0.dp), ) { ) { Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) } } Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt +74 −9 Original line number Original line Diff line number Diff line Loading @@ -37,11 +37,13 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role Loading @@ -51,8 +53,11 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.PlatformIconButton import com.android.compose.PlatformSliderColors import com.android.compose.PlatformSliderColors import com.android.compose.modifiers.padding 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.res.R import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel Loading Loading @@ -84,7 +89,11 @@ fun ColumnVolumeSliders( val sliderPadding by topSliderPadding(isExpandable) val sliderPadding by topSliderPadding(isExpandable) VolumeSlider( VolumeSlider( modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(), modifier = Modifier.thenIf(!Flags.volumeRedesign()) { Modifier.padding(end = { sliderPadding.roundToPx() }) } .fillMaxWidth(), state = sliderState, state = sliderState, onValueChange = { newValue: Float -> onValueChange = { newValue: Float -> sliderViewModel.onValueChanged(sliderState, newValue) sliderViewModel.onValueChanged(sliderState, newValue) Loading @@ -93,9 +102,22 @@ fun ColumnVolumeSliders( onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, sliderColors = sliderColors, sliderColors = sliderColors, hapticsViewModelFactory = sliderViewModel.getSliderHapticsViewModelFactory(), 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), modifier = Modifier.align(Alignment.CenterEnd), isExpanded = isExpanded, isExpanded = isExpanded, isExpandable = isExpandable, isExpandable = isExpandable, Loading @@ -103,6 +125,7 @@ fun ColumnVolumeSliders( sliderColors = sliderColors, sliderColors = sliderColors, ) ) } } } AnimatedVisibility( AnimatedVisibility( visible = isExpanded || !isExpandable, visible = isExpanded || !isExpandable, label = "CollapsableSliders", label = "CollapsableSliders", Loading Loading @@ -153,7 +176,7 @@ fun ColumnVolumeSliders( } } @Composable @Composable private fun ExpandButton( private fun ExpandButtonLegacy( isExpanded: Boolean, isExpanded: Boolean, isExpandable: Boolean, isExpandable: Boolean, onExpandedChanged: (Boolean) -> Unit, onExpandedChanged: (Boolean) -> Unit, Loading Loading @@ -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 { private fun enterTransition(index: Int, totalCount: Int): EnterTransition { val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0) val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0) val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100) val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100) Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +105 −2 Original line number Original line Diff line number Diff line Loading @@ -24,9 +24,18 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.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.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.Composable import androidx.compose.runtime.State import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue Loading @@ -48,6 +57,7 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors import com.android.compose.PlatformSliderColors import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.compose.modifiers.sysuiResTag Loading @@ -61,11 +71,104 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl fun VolumeSlider( fun VolumeSlider( state: SliderState, state: SliderState, onValueChange: (newValue: Float) -> Unit, onValueChange: (newValue: Float) -> Unit, onValueChangeFinished: (() -> Unit)? = null, onIconTapped: () -> Unit, onIconTapped: () -> Unit, sliderColors: PlatformSliderColors, modifier: Modifier = Modifier, 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, sliderColors: PlatformSliderColors, hapticsViewModelFactory: SliderHapticsViewModel.Factory?, hapticsViewModelFactory: SliderHapticsViewModel.Factory?, modifier: Modifier = Modifier, onValueChangeFinished: (() -> Unit)? = null, ) { ) { val value by valueState(state) val value by valueState(state) val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() } Loading Loading @@ -178,7 +281,7 @@ private fun valueState(state: SliderState): State<Float> { val shouldSkipAnimation = val shouldSkipAnimation = prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled val value = val value = if (shouldSkipAnimation) mutableFloatStateOf(state.value) if (shouldSkipAnimation) remember { mutableFloatStateOf(state.value) } else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation") else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation") prevState = state prevState = state return value return value Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt +3 −3 Original line number Original line Diff line number Diff line Loading @@ -53,7 +53,7 @@ private enum class VolumeSliderContentComponent { DisabledMessage, DisabledMessage, } } /** Shows label of the [VolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */ /** Shows label of the [LegacyVolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */ @Composable @Composable fun VolumeSliderContent( fun VolumeSliderContent( label: String, label: String, Loading Loading @@ -89,7 +89,7 @@ fun VolumeSliderContent( } } } } }, }, measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled) measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled), ) ) } } Loading @@ -102,7 +102,7 @@ private class VolumeSliderContentMeasurePolicy(private val isEnabled: Boolean) : override fun MeasureScope.measure( override fun MeasureScope.measure( measurables: List<Measurable>, measurables: List<Measurable>, constraints: Constraints constraints: Constraints, ): MeasureResult { ): MeasureResult { val labelPlaceable = val labelPlaceable = measurables measurables Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt +17 −4 Original line number Original line Diff line number Diff line Loading @@ -45,6 +45,7 @@ import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.animation.Expandable import com.android.compose.animation.Expandable import com.android.systemui.Flags import com.android.systemui.animation.Expandable import com.android.systemui.animation.Expandable import com.android.systemui.common.ui.compose.Icon 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.component.button.ui.viewmodel.ButtonViewModel Loading @@ -56,7 +57,7 @@ import kotlinx.coroutines.flow.StateFlow /** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */ /** [ComposeVolumePanelUiComponent] implementing a clickable button from a bottom row. */ class ButtonComponent( class ButtonComponent( private val viewModelFlow: StateFlow<ButtonViewModel?>, private val viewModelFlow: StateFlow<ButtonViewModel?>, private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit private val onClick: (expandable: Expandable, horizontalGravity: Int) -> Unit, ) : ComposeVolumePanelUiComponent { ) : ComposeVolumePanelUiComponent { @Composable @Composable Loading Loading @@ -84,14 +85,26 @@ class ButtonComponent( }, }, color = color = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.primary } else { MaterialTheme.colorScheme.tertiaryContainer MaterialTheme.colorScheme.tertiaryContainer } } else { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.surfaceContainerHigh } else { } else { MaterialTheme.colorScheme.surface MaterialTheme.colorScheme.surface } }, }, shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp), contentColor = contentColor = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { MaterialTheme.colorScheme.onPrimary } else { MaterialTheme.colorScheme.onTertiaryContainer MaterialTheme.colorScheme.onTertiaryContainer } } else { } else { MaterialTheme.colorScheme.onSurface MaterialTheme.colorScheme.onSurface }, }, Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt +25 −10 Original line number Original line Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.compose.ui.semantics.toggleableState import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.systemui.Flags import com.android.systemui.common.ui.compose.Icon 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.component.button.ui.viewmodel.ButtonViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent Loading @@ -51,7 +52,7 @@ import kotlinx.coroutines.flow.StateFlow /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */ /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */ class ToggleButtonComponent( class ToggleButtonComponent( private val viewModelFlow: StateFlow<ButtonViewModel?>, private val viewModelFlow: StateFlow<ButtonViewModel?>, private val onCheckedChange: (isChecked: Boolean) -> Unit private val onCheckedChange: (isChecked: Boolean) -> Unit, ) : ComposeVolumePanelUiComponent { ) : ComposeVolumePanelUiComponent { @Composable @Composable Loading @@ -68,16 +69,30 @@ class ToggleButtonComponent( BottomComponentButtonSurface { BottomComponentButtonSurface { val colors = val colors = if (viewModel.isActive) { if (viewModel.isActive) { if (Flags.volumeRedesign()) { ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary, ) } else { ButtonDefaults.buttonColors( ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.tertiaryContainer, containerColor = MaterialTheme.colorScheme.tertiaryContainer, contentColor = MaterialTheme.colorScheme.onTertiaryContainer, contentColor = MaterialTheme.colorScheme.onTertiaryContainer, ) ) } } else { if (Flags.volumeRedesign()) { ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.surfaceContainerHigh, contentColor = MaterialTheme.colorScheme.onSurface, ) } else { } else { ButtonDefaults.buttonColors( ButtonDefaults.buttonColors( containerColor = Color.Transparent, containerColor = Color.Transparent, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, contentColor = MaterialTheme.colorScheme.onSurfaceVariant, ) ) } } } Button( Button( modifier = modifier = Modifier.fillMaxSize().padding(8.dp).semantics { Modifier.fillMaxSize().padding(8.dp).semantics { Loading @@ -93,7 +108,7 @@ class ToggleButtonComponent( onClick = { onCheckedChange(!viewModel.isActive) }, onClick = { onCheckedChange(!viewModel.isActive) }, shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp), colors = colors, colors = colors, contentPadding = PaddingValues(0.dp) contentPadding = PaddingValues(0.dp), ) { ) { Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon) } } Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt +74 −9 Original line number Original line Diff line number Diff line Loading @@ -37,11 +37,13 @@ import androidx.compose.foundation.layout.size import androidx.compose.material3.Icon import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.IconButtonDefaults import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.Role Loading @@ -51,8 +53,11 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.PlatformIconButton import com.android.compose.PlatformSliderColors import com.android.compose.PlatformSliderColors import com.android.compose.modifiers.padding 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.res.R import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderViewModel Loading Loading @@ -84,7 +89,11 @@ fun ColumnVolumeSliders( val sliderPadding by topSliderPadding(isExpandable) val sliderPadding by topSliderPadding(isExpandable) VolumeSlider( VolumeSlider( modifier = Modifier.padding(end = { sliderPadding.roundToPx() }).fillMaxWidth(), modifier = Modifier.thenIf(!Flags.volumeRedesign()) { Modifier.padding(end = { sliderPadding.roundToPx() }) } .fillMaxWidth(), state = sliderState, state = sliderState, onValueChange = { newValue: Float -> onValueChange = { newValue: Float -> sliderViewModel.onValueChanged(sliderState, newValue) sliderViewModel.onValueChanged(sliderState, newValue) Loading @@ -93,9 +102,22 @@ fun ColumnVolumeSliders( onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, onIconTapped = { sliderViewModel.toggleMuted(sliderState) }, sliderColors = sliderColors, sliderColors = sliderColors, hapticsViewModelFactory = sliderViewModel.getSliderHapticsViewModelFactory(), 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), modifier = Modifier.align(Alignment.CenterEnd), isExpanded = isExpanded, isExpanded = isExpanded, isExpandable = isExpandable, isExpandable = isExpandable, Loading @@ -103,6 +125,7 @@ fun ColumnVolumeSliders( sliderColors = sliderColors, sliderColors = sliderColors, ) ) } } } AnimatedVisibility( AnimatedVisibility( visible = isExpanded || !isExpandable, visible = isExpanded || !isExpandable, label = "CollapsableSliders", label = "CollapsableSliders", Loading Loading @@ -153,7 +176,7 @@ fun ColumnVolumeSliders( } } @Composable @Composable private fun ExpandButton( private fun ExpandButtonLegacy( isExpanded: Boolean, isExpanded: Boolean, isExpandable: Boolean, isExpandable: Boolean, onExpandedChanged: (Boolean) -> Unit, onExpandedChanged: (Boolean) -> Unit, Loading Loading @@ -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 { private fun enterTransition(index: Int, totalCount: Int): EnterTransition { val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0) val enterDelay = ((totalCount - index + 1) * 10).coerceAtLeast(0) val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100) val enterDuration = (EXPAND_DURATION_MILLIS - enterDelay).coerceAtLeast(100) Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +105 −2 Original line number Original line Diff line number Diff line Loading @@ -24,9 +24,18 @@ import androidx.compose.animation.fadeOut import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box 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.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.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.Composable import androidx.compose.runtime.State import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue Loading @@ -48,6 +57,7 @@ import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors import com.android.compose.PlatformSliderColors import com.android.systemui.Flags import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.compose.modifiers.sysuiResTag Loading @@ -61,11 +71,104 @@ import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.Sl fun VolumeSlider( fun VolumeSlider( state: SliderState, state: SliderState, onValueChange: (newValue: Float) -> Unit, onValueChange: (newValue: Float) -> Unit, onValueChangeFinished: (() -> Unit)? = null, onIconTapped: () -> Unit, onIconTapped: () -> Unit, sliderColors: PlatformSliderColors, modifier: Modifier = Modifier, 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, sliderColors: PlatformSliderColors, hapticsViewModelFactory: SliderHapticsViewModel.Factory?, hapticsViewModelFactory: SliderHapticsViewModel.Factory?, modifier: Modifier = Modifier, onValueChangeFinished: (() -> Unit)? = null, ) { ) { val value by valueState(state) val value by valueState(state) val interactionSource = remember { MutableInteractionSource() } val interactionSource = remember { MutableInteractionSource() } Loading Loading @@ -178,7 +281,7 @@ private fun valueState(state: SliderState): State<Float> { val shouldSkipAnimation = val shouldSkipAnimation = prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled prevState is SliderState.Empty || prevState.isEnabled != state.isEnabled val value = val value = if (shouldSkipAnimation) mutableFloatStateOf(state.value) if (shouldSkipAnimation) remember { mutableFloatStateOf(state.value) } else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation") else animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation") prevState = state prevState = state return value return value Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSliderContent.kt +3 −3 Original line number Original line Diff line number Diff line Loading @@ -53,7 +53,7 @@ private enum class VolumeSliderContentComponent { DisabledMessage, DisabledMessage, } } /** Shows label of the [VolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */ /** Shows label of the [LegacyVolumeSlider]. Also shows [disabledMessage] when not [isEnabled]. */ @Composable @Composable fun VolumeSliderContent( fun VolumeSliderContent( label: String, label: String, Loading Loading @@ -89,7 +89,7 @@ fun VolumeSliderContent( } } } } }, }, measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled) measurePolicy = VolumeSliderContentMeasurePolicy(isEnabled), ) ) } } Loading @@ -102,7 +102,7 @@ private class VolumeSliderContentMeasurePolicy(private val isEnabled: Boolean) : override fun MeasureScope.measure( override fun MeasureScope.measure( measurables: List<Measurable>, measurables: List<Measurable>, constraints: Constraints constraints: Constraints, ): MeasureResult { ): MeasureResult { val labelPlaceable = val labelPlaceable = measurables measurables Loading