diff options
5 files changed, 88 insertions, 31 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index a46f4e5fef1a..cb3867f209e3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -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 @@ -60,14 +63,31 @@ fun VolumeSlider( PlatformSlider( modifier = modifier.clearAndSetSemantics { - if (!state.isEnabled) 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) + 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 } setProgress { targetValue -> val targetDirection = diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 45bcd829336f..9d7a44bd45ec 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1634,9 +1634,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> diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt index 038633810fd7..c08cd64e4e8c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt @@ -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( @@ -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), ) @@ -143,27 +175,14 @@ 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 (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") + if (audioStream.value in streamsAffectedByRing) { + if (ringerMode.value == AudioManager.RINGER_MODE_VIBRATE) { + R.drawable.ic_volume_ringer_vibrate + } else { R.drawable.ic_volume_off } + } else { + R.drawable.ic_volume_off } } else { iconsByStream[audioStream] @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt index 956ab66ac0c3..10714d1f41af 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/CastVolumeSliderViewModel.kt @@ -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, ) } @@ -85,6 +85,12 @@ constructor( override val isMutable: Boolean get() = false + + override val a11yClickDescription: String? + get() = null + + override val a11yStateDescription: String? + get() = null } @AssistedFactory diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt index d71a9d8dae94..c951928bb977 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderState.kt @@ -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 @@ -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 } |