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

Commit 387ff1fa authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Add custom a11y actions and state description for VolumeSlider" into main

parents 09273159 615c636c
Loading
Loading
Loading
Loading
+27 −7
Original line number Diff line number Diff line
@@ -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()
                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 =
+8 −2
Original line number Diff line number Diff line
@@ -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>
+42 −21
Original line number Diff line number Diff line
@@ -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,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 {
@@ -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
+7 −1
Original line number Diff line number Diff line
@@ -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
+4 −0
Original line number Diff line number Diff line
@@ -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
    }