Add a11y descriptions for the new panel
This CL makes sure Talkback properly announces the new Volume Panel components
Flag: aconfig new_volume_panel TEAMFOOD
Test: manual on phone with Talkback on
Fixes: 328456436
Change-Id: I89c534b12b30a3d49c873b94fb9aabdf1add00c3
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 98591e9..245dd79 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -910,7 +910,7 @@
<activity
android:name=".volume.panel.ui.activity.VolumePanelActivity"
- android:label="@string/sound_settings"
+ android:label="@string/accessibility_volume_settings"
android:excludeFromRecents="true"
android:exported="false"
android:launchMode="singleInstance"
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
index 5f7bd47..b721e41 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt
@@ -32,6 +32,11 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Expandable
@@ -52,6 +57,7 @@
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
val viewModelByState by viewModelFlow.collectAsState()
val viewModel = viewModelByState ?: return
+ val label = viewModel.label.toString()
Column(
modifier = modifier,
@@ -59,7 +65,11 @@
horizontalAlignment = Alignment.CenterHorizontally,
) {
Expandable(
- modifier = Modifier.height(64.dp).fillMaxWidth(),
+ modifier =
+ Modifier.height(64.dp).fillMaxWidth().semantics {
+ role = Role.Button
+ contentDescription = label
+ },
color = MaterialTheme.colorScheme.primaryContainer,
shape = RoundedCornerShape(28.dp),
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
@@ -71,7 +81,8 @@
}
}
Text(
- text = viewModel.label.toString(),
+ modifier = Modifier.clearAndSetSemantics {},
+ text = label,
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
index dfee684..28fd785 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt
@@ -32,6 +32,9 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.android.systemui.common.ui.compose.Icon
@@ -50,13 +53,16 @@
override fun VolumePanelComposeScope.Content(modifier: Modifier) {
val viewModelByState by viewModelFlow.collectAsState()
val viewModel = viewModelByState ?: return
+ val label = viewModel.label.toString()
+
Column(
modifier = modifier,
verticalArrangement = Arrangement.spacedBy(12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
OutlinedIconToggleButton(
- modifier = Modifier.height(64.dp).fillMaxWidth(),
+ modifier =
+ Modifier.height(64.dp).fillMaxWidth().semantics { contentDescription = label },
checked = viewModel.isChecked,
onCheckedChange = onCheckedChange,
shape = RoundedCornerShape(28.dp),
@@ -72,7 +78,8 @@
Icon(modifier = Modifier.size(24.dp), icon = viewModel.icon)
}
Text(
- text = viewModel.label.toString(),
+ modifier = Modifier.clearAndSetSemantics {},
+ text = label,
style = MaterialTheme.typography.labelMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
index 53de5bc..6f2ed81 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/mediaoutput/ui/composable/MediaOutputComponent.kt
@@ -49,10 +49,16 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.LiveRegionMode
+import androidx.compose.ui.semantics.liveRegion
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import com.android.compose.animation.Expandable
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.toColor
+import com.android.systemui.res.R
import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.ConnectedDeviceViewModel
import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.DeviceIconViewModel
import com.android.systemui.volume.panel.component.mediaoutput.ui.viewmodel.MediaOutputViewModel
@@ -74,14 +80,19 @@
viewModel.connectedDeviceViewModel.collectAsState()
val deviceIconViewModel: DeviceIconViewModel? by
viewModel.deviceIconViewModel.collectAsState()
+ val clickLabel = stringResource(R.string.volume_panel_enter_media_output_settings)
Expandable(
- modifier = Modifier.fillMaxWidth().height(80.dp),
+ modifier =
+ Modifier.fillMaxWidth().height(80.dp).semantics {
+ liveRegion = LiveRegionMode.Polite
+ this.onClick(label = clickLabel) { false }
+ },
color = MaterialTheme.colorScheme.surface,
shape = RoundedCornerShape(28.dp),
onClick = { viewModel.onBarClick(it) },
) { _ ->
- Row(verticalAlignment = Alignment.CenterVertically) {
+ Row(modifier = Modifier, verticalAlignment = Alignment.CenterVertically) {
connectedDeviceViewModel?.let { ConnectedDeviceText(it) }
deviceIconViewModel?.let { ConnectedDeviceIcon(it) }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
index 89251939..26086d1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/popup/ui/composable/VolumePanelPopup.kt
@@ -24,13 +24,16 @@
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.wrapContentHeight
+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.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
-import com.android.compose.PlatformIconButton
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.res.R
@@ -101,16 +104,19 @@
}
}
- PlatformIconButton(
+ IconButton(
modifier = Modifier.align(Alignment.TopEnd).size(64.dp).padding(20.dp),
- iconResource = R.drawable.ic_close,
- contentDescription = null,
onClick = { dialog.dismiss() },
colors =
IconButtonDefaults.iconButtonColors(
contentColor = MaterialTheme.colorScheme.outline
)
- )
+ ) {
+ Icon(
+ painterResource(R.drawable.ic_close),
+ contentDescription = stringResource(R.string.accessibility_desc_close),
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
index a787c50..e1cf3a5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt
@@ -45,6 +45,11 @@
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.role
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.semantics.stateDescription
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformSliderColors
import com.android.systemui.res.R
@@ -83,12 +88,19 @@
sliderColors = sliderColors,
)
+ val expandButtonStateDescription =
+ if (isExpanded) stringResource(R.string.volume_panel_expanded_sliders)
+ else stringResource(R.string.volume_panel_collapsed_sliders)
if (isExpandable) {
ExpandButton(
+ modifier =
+ Modifier.semantics {
+ role = Role.Switch
+ stateDescription = expandButtonStateDescription
+ },
isExpanded = isExpanded,
onExpandedChanged = onExpandedChanged,
- sliderColors,
- Modifier,
+ sliderColors = sliderColors,
)
}
}
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 f9a712b..fa94ea0 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
@@ -33,6 +33,12 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.semantics.ProgressBarRangeInfo
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.disabled
+import androidx.compose.ui.semantics.progressBarRangeInfo
+import androidx.compose.ui.semantics.setProgress
import androidx.compose.ui.unit.dp
import com.android.compose.PlatformSlider
import com.android.compose.PlatformSliderColors
@@ -50,7 +56,31 @@
val value by
animateFloatAsState(targetValue = state.value, label = "VolumeSliderValueAnimation")
PlatformSlider(
- modifier = modifier,
+ modifier =
+ modifier.clearAndSetSemantics {
+ if (!state.isEnabled) disabled()
+ contentDescription = state.label
+
+ // 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)
+ 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
+ }
+ },
value = value,
valueRange = state.valueRange,
onValueChange = onValueChange,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
index 681c839..79d3fe9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractorTest.kt
@@ -30,18 +30,8 @@
private val underTest = VolumeSliderInteractor()
@Test
- fun translateValueToVolume() {
- assertThat(underTest.translateValueToVolume(30f, volumeRange)).isEqualTo(3)
- }
-
- @Test
- fun processVolumeToValue_muted_zero() {
- assertThat(underTest.processVolumeToValue(3, volumeRange, true)).isEqualTo(0)
- }
-
- @Test
fun processVolumeToValue_returnsTranslatedVolume() {
- assertThat(underTest.processVolumeToValue(2, volumeRange, false)).isEqualTo(20f)
+ assertThat(underTest.processVolumeToValue(2, volumeRange)).isEqualTo(20f)
}
private companion object {
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index b8f20f6..e3a5e15 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1556,11 +1556,11 @@
<string name="volume_panel_noise_control_title">Noise Control</string>
<!-- Label for button to enabled/disable spatial audio [CHAR_LIMIT=30] -->
<string name="volume_panel_spatial_audio_title">Spatial Audio</string>
- <!-- Label for button to disable spatial audio [CHAR_LIMIT=20] -->
+ <!-- Label for a spatial audio button for the case when it is disabled [CHAR_LIMIT=20] -->
<string name="volume_panel_spatial_audio_off">Off</string>
- <!-- Label for button to enabled spatial audio [CHAR_LIMIT=20] -->
+ <!-- Label for a spatial audio button for the case when it is enabled without head tracking [CHAR_LIMIT=20] -->
<string name="volume_panel_spatial_audio_fixed">Fixed</string>
- <!-- Label for button to enabled head tracking [CHAR_LIMIT=20] -->
+ <!-- Label for a spatial audio button for the case when it is enabled with head tracking [CHAR_LIMIT=20] -->
<string name="volume_panel_spatial_audio_tracking">Head Tracking</string>
<string name="volume_ringer_change">Tap to change ringer mode</string>
@@ -1576,6 +1576,18 @@
<string name="volume_dialog_ringer_guidance_ring">Calls and notifications will ring (<xliff:g id="volume level" example="56">%1$s</xliff:g>)</string>
+ <!-- An audible a11y label for a button, that opens settings when clicked [CHAR_LIMIT=NONE] -->
+ <string name="volume_panel_enter_media_output_settings">Enter output settings</string>
+ <!-- An audible a11y state description for a button, that expands volume sliders menu [CHAR_LIMIT=NONE] -->
+ <string name="volume_panel_expanded_sliders">Volume sliders expanded</string>
+ <!-- An audible a11y state description for a button, that collapses volume sliders menu [CHAR LIMIT=NONE] -->
+ <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>
+ <!-- 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>
+
<!-- Title with application label for media output settings. [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/domain/interactor/VolumeSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
index e37a8cf..ecd89ea 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/domain/interactor/VolumeSliderInteractor.kt
@@ -24,69 +24,24 @@
class VolumeSliderInteractor @Inject constructor() {
/** mimic percentage volume setting */
- val displayValueRange: ClosedFloatingPointRange<Float> = 0f..100f
+ private val displayValueRange: ClosedFloatingPointRange<Float> = 0f..100f
/**
* Translates [volume], that belongs to [volumeRange] to the value that belongs to
* [displayValueRange].
- *
- * [currentValue] is the raw value received from the slider. Returns [currentValue] when it
- * translates to the same volume as [volume] parameter. This ensures smooth slider experience
- * (avoids snapping when the user stops dragging).
*/
fun processVolumeToValue(
volume: Int,
volumeRange: ClosedRange<Int>,
- isMuted: Boolean,
): Float {
- if (isMuted) {
- return 0f
- }
- return translateToRange(
- currentValue = volume.toFloat(),
- currentRangeStart = volumeRange.start.toFloat(),
- currentRangeEnd = volumeRange.endInclusive.toFloat(),
- targetRangeStart = displayValueRange.start,
- targetRangeEnd = displayValueRange.endInclusive,
- )
- }
-
- /** Translates [value] from [displayValueRange] to volume that has [volumeRange]. */
- fun translateValueToVolume(
- value: Float,
- volumeRange: ClosedRange<Int>,
- ): Int {
- return translateToRange(
- currentValue = value,
- currentRangeStart = displayValueRange.start,
- currentRangeEnd = displayValueRange.endInclusive,
- targetRangeStart = volumeRange.start.toFloat(),
- targetRangeEnd = volumeRange.endInclusive.toFloat(),
- )
- .toInt()
- }
-
- /**
- * Translates a value from one range to another.
- *
- * ```
- * Given: currentValue=3, currentRange=[0, 8], targetRange=[0, 100]
- * Result: 37.5
- * ```
- */
- private fun translateToRange(
- currentValue: Float,
- currentRangeStart: Float,
- currentRangeEnd: Float,
- targetRangeStart: Float,
- targetRangeEnd: Float,
- ): Float {
- val currentRangeLength: Float = (currentRangeEnd - currentRangeStart)
- val targetRangeLength: Float = targetRangeEnd - targetRangeStart
+ val currentRangeStart: Float = volumeRange.start.toFloat()
+ val targetRangeStart: Float = displayValueRange.start
+ val currentRangeLength: Float = (volumeRange.endInclusive.toFloat() - currentRangeStart)
+ val targetRangeLength: Float = displayValueRange.endInclusive - targetRangeStart
if (currentRangeLength == 0f || targetRangeLength == 0f) {
return 0f
}
- val volumeFraction: Float = (currentValue - currentRangeStart) / currentRangeLength
+ val volumeFraction: Float = (volume.toFloat() - currentRangeStart) / currentRangeLength
return targetRangeStart + volumeFraction * targetRangeLength
}
}
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 74a8a27..1b73208 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
@@ -28,6 +28,7 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -85,12 +86,7 @@
val audioViewModel = state as? State
audioViewModel ?: return
coroutineScope.launch {
- val volume =
- volumeSliderInteractor.translateValueToVolume(
- newValue,
- audioViewModel.audioStreamModel.volumeRange
- )
- audioVolumeInteractor.setVolume(audioStream, volume)
+ audioVolumeInteractor.setVolume(audioStream, newValue.roundToInt())
}
}
@@ -107,13 +103,18 @@
ringerMode: RingerMode,
): State {
return State(
- value = volumeSliderInteractor.processVolumeToValue(volume, volumeRange, isMuted),
- valueRange = volumeSliderInteractor.displayValueRange,
+ value = volume.toFloat(),
+ valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
+ valueText =
+ SliderViewModel.formatValue(
+ volumeSliderInteractor.processVolumeToValue(volume, volumeRange)
+ ),
icon = getIcon(ringerMode),
label = labelsByStream[audioStream]?.let(context::getString)
?: error("No label for the stream: $audioStream"),
disabledMessage = disabledTextByStream[audioStream]?.let(context::getString),
isEnabled = isEnabled,
+ a11yStep = volumeRange.step,
audioStreamModel = this,
)
}
@@ -155,8 +156,10 @@
override val valueRange: ClosedFloatingPointRange<Float>,
override val icon: Icon,
override val label: String,
+ override val valueText: String,
override val disabledMessage: String?,
override val isEnabled: Boolean,
+ override val a11yStep: Int,
val audioStreamModel: AudioStreamModel,
) : SliderState
@@ -164,8 +167,10 @@
override val value: Float = 0f
override val valueRange: ClosedFloatingPointRange<Float> = 0f..1f
override val icon: Icon? = null
+ override val valueText: String = ""
override val label: String = ""
override val disabledMessage: String? = null
+ override val a11yStep: Int = 0
override val isEnabled: Boolean = true
}
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 efee100..86b2d73 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
@@ -26,6 +26,7 @@
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
+import kotlin.math.roundToInt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -52,10 +53,7 @@
override fun onValueChanged(state: SliderState, newValue: Float) {
coroutineScope.launch {
- castVolumeInteractor.setVolume(
- routingSession,
- volumeSliderInteractor.translateValueToVolume(newValue, volumeRange),
- )
+ castVolumeInteractor.setVolume(routingSession, newValue.roundToInt())
}
}
@@ -65,24 +63,29 @@
private fun getCurrentState(): State =
State(
- value =
- volumeSliderInteractor.processVolumeToValue(
- volume = routingSession.routingSessionInfo.volume,
- volumeRange = volumeRange,
- isMuted = false,
- ),
- valueRange = volumeSliderInteractor.displayValueRange,
+ value = routingSession.routingSessionInfo.volume.toFloat(),
+ valueRange = volumeRange.first.toFloat()..volumeRange.last.toFloat(),
icon = Icon.Resource(R.drawable.ic_cast, null),
+ valueText =
+ SliderViewModel.formatValue(
+ volumeSliderInteractor.processVolumeToValue(
+ volume = routingSession.routingSessionInfo.volume,
+ volumeRange = volumeRange,
+ )
+ ),
label = context.getString(R.string.media_device_cast),
isEnabled = true,
+ a11yStep = 1
)
private data class State(
override val value: Float,
override val valueRange: ClosedFloatingPointRange<Float>,
override val icon: Icon,
+ override val valueText: String,
override val label: String,
override val isEnabled: Boolean,
+ override val a11yStep: Int,
) : SliderState {
override val disabledMessage: String?
get() = null
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 6e9794b..b87d0a7 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
@@ -27,7 +27,13 @@
val value: Float
val valueRange: ClosedFloatingPointRange<Float>
val icon: Icon?
- val label: String
- val disabledMessage: String?
val isEnabled: Boolean
+ val valueText: String
+ val label: String
+ /**
+ * A11y slider controls works by adjusting one step up or down. The default slider step isn't
+ * enough to trigger rounding to the correct value.
+ */
+ val a11yStep: Int
+ val disabledMessage: String?
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
index 74aee55..e78f833 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/SliderViewModel.kt
@@ -26,4 +26,9 @@
fun onValueChanged(state: SliderState, newValue: Float)
fun toggleMuted(state: SliderState)
+
+ companion object {
+
+ fun formatValue(value: Float): String = "%.0f".format(value)
+ }
}