Loading packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +27 −7 Original line number Diff line number Diff line Loading @@ -34,12 +34,15 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.ProgressBarRangeInfo import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors Loading @@ -60,14 +63,31 @@ fun VolumeSlider( PlatformSlider( modifier = modifier.clearAndSetSemantics { if (!state.isEnabled) disabled() if (state.isEnabled) { contentDescription = state.label state.a11yClickDescription?.let { customActions = listOf( CustomAccessibilityAction( it, ) { onIconTapped() true } ) } state.a11yStateDescription?.let { stateDescription = it } ?: run { // provide a not animated value to the a11y because it fails to announce // the settled value when it changes rapidly. progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange) } } else { disabled() contentDescription = state.disabledMessage?.let { "${state.label}, $it" } ?: state.label // provide a not animated value to the a11y because it fails to announce the // settled value when it changes rapidly. if (state.isEnabled) { progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange) } setProgress { targetValue -> val targetDirection = Loading packages/SystemUI/res/values/strings.xml +8 −2 Original line number Diff line number Diff line Loading @@ -1636,9 +1636,15 @@ <string name="volume_panel_collapsed_sliders">Volume sliders collapsed</string> <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to mute media [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_mute">mute %s</string> <string name="volume_panel_hint_mute">Mute %s</string> <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to unmute media [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_unmute">unmute %s</string> <string name="volume_panel_hint_unmute">Unmute %s</string> <!-- Hint for accessibility. This is announced when the stream is muted [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_muted">muted</string> <!-- Hint for accessibility. This is announced when ring mode is set to Vibrate. [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_vibrate">vibrate</string> <!-- Title with application label for media output settings when there is media playing. [CHAR LIMIT=20] --> <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string> Loading packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +42 −21 Original line number Diff line number Diff line Loading @@ -49,6 +49,11 @@ constructor( private val uiEventLogger: UiEventLogger, ) : SliderViewModel { private val streamsAffectedByRing = setOf( AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION, ) private val audioStream = audioStreamWrapper.audioStream private val iconsByStream = mapOf( Loading Loading @@ -125,15 +130,42 @@ constructor( isEnabled: Boolean, ringerMode: RingerMode, ): State { val label = labelsByStream[audioStream]?.let(context::getString) ?: error("No label for the stream: $audioStream") return State( value = volume.toFloat(), valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(), icon = getIcon(ringerMode), label = labelsByStream[audioStream]?.let(context::getString) ?: error("No label for the stream: $audioStream"), label = label, disabledMessage = disabledTextByStream[audioStream]?.let(context::getString), isEnabled = isEnabled, a11yStep = volumeRange.step, a11yClickDescription = context.getString( if (isMuted) { R.string.volume_panel_hint_unmute } else { R.string.volume_panel_hint_mute }, label, ), a11yStateDescription = if (volume == volumeRange.first) { context.getString( if (audioStream.value in streamsAffectedByRing) { if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.string.volume_panel_hint_vibrate } else { R.string.volume_panel_hint_muted } } else { R.string.volume_panel_hint_muted } ) } else { null }, audioStreamModel = this, isMutable = audioVolumeInteractor.isAffectedByMute(audioStream), ) Loading @@ -143,28 +175,15 @@ constructor( val isMutedOrNoVolume = isMuted || volume == minVolume val iconRes = if (isMutedOrNoVolume) { when (audioStream.value) { AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_off AudioManager.STREAM_BLUETOOTH_SCO -> R.drawable.ic_volume_off AudioManager.STREAM_VOICE_CALL -> R.drawable.ic_volume_off AudioManager.STREAM_RING -> if (audioStream.value in streamsAffectedByRing) { if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.drawable.ic_volume_ringer_vibrate } else { R.drawable.ic_volume_off } AudioManager.STREAM_NOTIFICATION -> if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.drawable.ic_volume_ringer_vibrate } else { R.drawable.ic_volume_off } AudioManager.STREAM_ALARM -> R.drawable.ic_volume_off else -> { Log.wtf(TAG, "No icon for the stream: $audioStream") R.drawable.ic_volume_off } } } else { iconsByStream[audioStream] ?: run { Loading @@ -186,6 +205,8 @@ constructor( override val disabledMessage: String?, override val isEnabled: Boolean, override val a11yStep: Int, override val a11yClickDescription: String?, override val a11yStateDescription: String?, override val isMutable: Boolean, val audioStreamModel: AudioStreamModel, ) : SliderState Loading packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt +7 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ constructor( icon = Icon.Resource(R.drawable.ic_cast, null), label = context.getString(R.string.media_device_cast), isEnabled = true, a11yStep = 1 a11yStep = 1, ) } Loading @@ -85,6 +85,12 @@ constructor( override val isMutable: Boolean get() = false override val a11yClickDescription: String? get() = null override val a11yStateDescription: String? get() = null } @AssistedFactory Loading packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ sealed interface SliderState { * enough to trigger rounding to the correct value. */ val a11yStep: Int val a11yClickDescription: String? val a11yStateDescription: String? val disabledMessage: String? val isMutable: Boolean Loading @@ -44,6 +46,8 @@ sealed interface SliderState { override val label: String = "" override val disabledMessage: String? = null override val a11yStep: Int = 0 override val a11yClickDescription: String? = null override val a11yStateDescription: String? = null override val isEnabled: Boolean = true override val isMutable: Boolean = false } Loading Loading
packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +27 −7 Original line number Diff line number Diff line Loading @@ -34,12 +34,15 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.semantics.CustomAccessibilityAction import androidx.compose.ui.semantics.ProgressBarRangeInfo import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.progressBarRangeInfo import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.semantics.stateDescription import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors Loading @@ -60,14 +63,31 @@ fun VolumeSlider( PlatformSlider( modifier = modifier.clearAndSetSemantics { if (!state.isEnabled) disabled() if (state.isEnabled) { contentDescription = state.label state.a11yClickDescription?.let { customActions = listOf( CustomAccessibilityAction( it, ) { onIconTapped() true } ) } state.a11yStateDescription?.let { stateDescription = it } ?: run { // provide a not animated value to the a11y because it fails to announce // the settled value when it changes rapidly. progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange) } } else { disabled() contentDescription = state.disabledMessage?.let { "${state.label}, $it" } ?: state.label // provide a not animated value to the a11y because it fails to announce the // settled value when it changes rapidly. if (state.isEnabled) { progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange) } setProgress { targetValue -> val targetDirection = Loading
packages/SystemUI/res/values/strings.xml +8 −2 Original line number Diff line number Diff line Loading @@ -1636,9 +1636,15 @@ <string name="volume_panel_collapsed_sliders">Volume sliders collapsed</string> <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to mute media [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_mute">mute %s</string> <string name="volume_panel_hint_mute">Mute %s</string> <!-- Hint for accessibility. A stream name is a parameter. For example: double tap to unmute media [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_unmute">unmute %s</string> <string name="volume_panel_hint_unmute">Unmute %s</string> <!-- Hint for accessibility. This is announced when the stream is muted [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_muted">muted</string> <!-- Hint for accessibility. This is announced when ring mode is set to Vibrate. [CHAR_LIMIT=NONE] --> <string name="volume_panel_hint_vibrate">vibrate</string> <!-- Title with application label for media output settings when there is media playing. [CHAR LIMIT=20] --> <string name="media_output_label_title">Playing <xliff:g id="label" example="Music Player">%s</xliff:g> on</string> Loading
packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +42 −21 Original line number Diff line number Diff line Loading @@ -49,6 +49,11 @@ constructor( private val uiEventLogger: UiEventLogger, ) : SliderViewModel { private val streamsAffectedByRing = setOf( AudioManager.STREAM_RING, AudioManager.STREAM_NOTIFICATION, ) private val audioStream = audioStreamWrapper.audioStream private val iconsByStream = mapOf( Loading Loading @@ -125,15 +130,42 @@ constructor( isEnabled: Boolean, ringerMode: RingerMode, ): State { val label = labelsByStream[audioStream]?.let(context::getString) ?: error("No label for the stream: $audioStream") return State( value = volume.toFloat(), valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(), icon = getIcon(ringerMode), label = labelsByStream[audioStream]?.let(context::getString) ?: error("No label for the stream: $audioStream"), label = label, disabledMessage = disabledTextByStream[audioStream]?.let(context::getString), isEnabled = isEnabled, a11yStep = volumeRange.step, a11yClickDescription = context.getString( if (isMuted) { R.string.volume_panel_hint_unmute } else { R.string.volume_panel_hint_mute }, label, ), a11yStateDescription = if (volume == volumeRange.first) { context.getString( if (audioStream.value in streamsAffectedByRing) { if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.string.volume_panel_hint_vibrate } else { R.string.volume_panel_hint_muted } } else { R.string.volume_panel_hint_muted } ) } else { null }, audioStreamModel = this, isMutable = audioVolumeInteractor.isAffectedByMute(audioStream), ) Loading @@ -143,28 +175,15 @@ constructor( val isMutedOrNoVolume = isMuted || volume == minVolume val iconRes = if (isMutedOrNoVolume) { when (audioStream.value) { AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_off AudioManager.STREAM_BLUETOOTH_SCO -> R.drawable.ic_volume_off AudioManager.STREAM_VOICE_CALL -> R.drawable.ic_volume_off AudioManager.STREAM_RING -> if (audioStream.value in streamsAffectedByRing) { if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.drawable.ic_volume_ringer_vibrate } else { R.drawable.ic_volume_off } AudioManager.STREAM_NOTIFICATION -> if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { R.drawable.ic_volume_ringer_vibrate } else { R.drawable.ic_volume_off } AudioManager.STREAM_ALARM -> R.drawable.ic_volume_off else -> { Log.wtf(TAG, "No icon for the stream: $audioStream") R.drawable.ic_volume_off } } } else { iconsByStream[audioStream] ?: run { Loading @@ -186,6 +205,8 @@ constructor( override val disabledMessage: String?, override val isEnabled: Boolean, override val a11yStep: Int, override val a11yClickDescription: String?, override val a11yStateDescription: String?, override val isMutable: Boolean, val audioStreamModel: AudioStreamModel, ) : SliderState Loading
packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt +7 −1 Original line number Diff line number Diff line Loading @@ -68,7 +68,7 @@ constructor( icon = Icon.Resource(R.drawable.ic_cast, null), label = context.getString(R.string.media_device_cast), isEnabled = true, a11yStep = 1 a11yStep = 1, ) } Loading @@ -85,6 +85,12 @@ constructor( override val isMutable: Boolean get() = false override val a11yClickDescription: String? get() = null override val a11yStateDescription: String? get() = null } @AssistedFactory Loading
packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,8 @@ sealed interface SliderState { * enough to trigger rounding to the correct value. */ val a11yStep: Int val a11yClickDescription: String? val a11yStateDescription: String? val disabledMessage: String? val isMutable: Boolean Loading @@ -44,6 +46,8 @@ sealed interface SliderState { override val label: String = "" override val disabledMessage: String? = null override val a11yStep: Int = 0 override val a11yClickDescription: String? = null override val a11yStateDescription: String? = null override val isEnabled: Boolean = true override val isMutable: Boolean = false } Loading