From 60951cc065c9bbda9dee934e4d9b88efaabb5b5d Mon Sep 17 00:00:00 2001 From: dstefan Date: Fri, 17 Jul 2026 14:46:12 +0300 Subject: [PATCH 1/4] Centralize volume button accessibility semantics in VolumeScreen --- gradle.properties | 3 ++ .../audio/ui/material3/VolumeScreen.kt | 46 ++++++++++++++++++- .../horologist/audio/ui/VolumeScreen.kt | 38 +++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index ca2a3be9c8..27f4377cd1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -72,3 +72,6 @@ strict.build=false # media3 checkout media3Checkout= + +# Enabled parallel sync for Gradle 9.4+ +org.gradle.tooling.parallel=true diff --git a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt index b1347d60a9..f8bb3e817b 100644 --- a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt +++ b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt @@ -38,8 +38,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.onClick +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 @@ -292,6 +296,10 @@ public fun VolumeScreen( if (showVolumeIndicator) { VolumeLevelIndicator(volumeUiState = volume, colorScheme = colorScheme) } + val increaseContentDesc = + stringResource(id = R.string.horologist_volume_screen_volume_up_content_description) + val decreaseContentDesc = + stringResource(id = R.string.horologist_volume_screen_volume_down_content_description) Stepper( modifier = modifier.semantics { @@ -301,8 +309,42 @@ public fun VolumeScreen( value = currentValue, onValueChange = { if (it > volumeState.current) increaseVolume() else decreaseVolume() }, steps = volumeState.max - 1, - increaseIcon = { increaseIcon() }, - decreaseIcon = { decreaseIcon() }, + increaseIcon = { + Box( + modifier = + Modifier.clearAndSetSemantics { + contentDescription = increaseContentDesc + role = Role.Button + onClick( + label = increaseContentDesc, + action = { + increaseVolume() + true + }, + ) + } + ) { + increaseIcon() + } + }, + decreaseIcon = { + Box( + modifier = + Modifier.clearAndSetSemantics { + contentDescription = decreaseContentDesc + role = Role.Button + onClick( + label = decreaseContentDesc, + action = { + decreaseVolume() + true + }, + ) + } + ) { + decreaseIcon() + } + }, colors = StepperDefaults.colors( contentColor = colorScheme.onSurface, diff --git a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt index 8315439549..44dd5c8ef3 100644 --- a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt +++ b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt @@ -19,6 +19,10 @@ package com.google.android.horologist.audio.ui import android.media.AudioManager +import androidx.compose.ui.semantics.clearAndSetSemantics +import androidx.compose.foundation.layout.Box +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.onClick import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -206,6 +210,10 @@ internal fun VolumeScreen( } else { stringResource(id = R.string.horologist_volume_screen_volume_percent, volumePercent) } + val increaseContentDesc = + stringResource(id = R.string.horologist_volume_screen_volume_up_content_description) + val decreaseContentDesc = + stringResource(id = R.string.horologist_volume_screen_volume_down_content_description) Stepper( modifier = modifier.semantics { liveRegion = LiveRegionMode.Assertive @@ -216,10 +224,40 @@ internal fun VolumeScreen( steps = volumeState.max - 1, valueRange = (0f..volumeState.max.toFloat()), increaseIcon = { + Box( + modifier = + Modifier.clearAndSetSemantics { + contentDescription = increaseContentDesc + role = Role.Button + onClick( + label = increaseContentDesc, + action = { + increaseVolume() + true + }, + ) + } + ) { increaseIcon() + } }, decreaseIcon = { + Box( + modifier = + Modifier.clearAndSetSemantics { + contentDescription = decreaseContentDesc + role = Role.Button + onClick( + label = decreaseContentDesc, + action = { + decreaseVolume() + true + }, + ) + } + ) { decreaseIcon() + } }, enableRangeSemantics = false, ) { From 3d39e68c05809b3023af662b6a37e9259a66563b Mon Sep 17 00:00:00 2001 From: dstefan Date: Mon, 27 Jul 2026 17:52:44 +0300 Subject: [PATCH 2/4] Centralize volume button accessibility semantics in VolumeScreen --- .../horologist/audio/ui/material3/VolumeScreen.kt | 14 -------------- .../android/horologist/audio/ui/VolumeScreen.kt | 14 -------------- 2 files changed, 28 deletions(-) diff --git a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt index f8bb3e817b..5069b66525 100644 --- a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt +++ b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt @@ -315,13 +315,6 @@ public fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc role = Role.Button - onClick( - label = increaseContentDesc, - action = { - increaseVolume() - true - }, - ) } ) { increaseIcon() @@ -333,13 +326,6 @@ public fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc role = Role.Button - onClick( - label = decreaseContentDesc, - action = { - decreaseVolume() - true - }, - ) } ) { decreaseIcon() diff --git a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt index 44dd5c8ef3..3941a7da26 100644 --- a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt +++ b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt @@ -229,13 +229,6 @@ internal fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc role = Role.Button - onClick( - label = increaseContentDesc, - action = { - increaseVolume() - true - }, - ) } ) { increaseIcon() @@ -247,13 +240,6 @@ internal fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc role = Role.Button - onClick( - label = decreaseContentDesc, - action = { - decreaseVolume() - true - }, - ) } ) { decreaseIcon() From 47199b99e1db8a3ec6e73c1dda8af25ffb87f198 Mon Sep 17 00:00:00 2001 From: dstefan Date: Mon, 27 Jul 2026 17:52:44 +0300 Subject: [PATCH 3/4] Centralize volume button accessibility semantics in VolumeScreen - remove on click --- .../horologist/audio/ui/material3/VolumeScreen.kt | 14 -------------- .../android/horologist/audio/ui/VolumeScreen.kt | 14 -------------- 2 files changed, 28 deletions(-) diff --git a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt index f8bb3e817b..5069b66525 100644 --- a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt +++ b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt @@ -315,13 +315,6 @@ public fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc role = Role.Button - onClick( - label = increaseContentDesc, - action = { - increaseVolume() - true - }, - ) } ) { increaseIcon() @@ -333,13 +326,6 @@ public fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc role = Role.Button - onClick( - label = decreaseContentDesc, - action = { - decreaseVolume() - true - }, - ) } ) { decreaseIcon() diff --git a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt index 44dd5c8ef3..3941a7da26 100644 --- a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt +++ b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt @@ -229,13 +229,6 @@ internal fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc role = Role.Button - onClick( - label = increaseContentDesc, - action = { - increaseVolume() - true - }, - ) } ) { increaseIcon() @@ -247,13 +240,6 @@ internal fun VolumeScreen( Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc role = Role.Button - onClick( - label = decreaseContentDesc, - action = { - decreaseVolume() - true - }, - ) } ) { decreaseIcon() From 56321906b9ae38dcc3768d56285a3c2fe9cd5ab9 Mon Sep 17 00:00:00 2001 From: dstefan Date: Tue, 28 Jul 2026 19:25:25 +0300 Subject: [PATCH 4/4] Centralize volume button accessibility semantics in VolumeScreen Removed role = Role.Button inside clearAndSetSemantics for both increaseIcon and decreaseIcon slots so only the outer Stepper button container provides the button role: --- .../horologist/audio/ui/material3/VolumeScreen.kt | 5 ----- .../android/horologist/audio/ui/VolumeScreen.kt | 12 +++--------- 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt index 5069b66525..a1c80742ec 100644 --- a/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt +++ b/media/audio-ui-material3/src/main/java/com/google/android/horologist/audio/ui/material3/VolumeScreen.kt @@ -38,12 +38,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.LiveRegionMode -import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.liveRegion -import androidx.compose.ui.semantics.onClick -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 @@ -314,7 +311,6 @@ public fun VolumeScreen( modifier = Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc - role = Role.Button } ) { increaseIcon() @@ -325,7 +321,6 @@ public fun VolumeScreen( modifier = Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc - role = Role.Button } ) { decreaseIcon() diff --git a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt index 3941a7da26..7eaa3876cb 100644 --- a/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt +++ b/media/audio-ui/src/main/java/com/google/android/horologist/audio/ui/VolumeScreen.kt @@ -19,10 +19,7 @@ package com.google.android.horologist.audio.ui import android.media.AudioManager -import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.foundation.layout.Box -import androidx.compose.ui.semantics.liveRegion -import androidx.compose.ui.semantics.onClick import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons @@ -37,10 +34,9 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.LiveRegionMode -import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.liveRegion -import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow @@ -228,7 +224,6 @@ internal fun VolumeScreen( modifier = Modifier.clearAndSetSemantics { contentDescription = increaseContentDesc - role = Role.Button } ) { increaseIcon() @@ -239,7 +234,6 @@ internal fun VolumeScreen( modifier = Modifier.clearAndSetSemantics { contentDescription = decreaseContentDesc - role = Role.Button } ) { decreaseIcon() @@ -261,7 +255,7 @@ public object VolumeScreenDefaults { @Composable public fun IncreaseIcon() { Icon( - modifier = Modifier.size(26.dp).semantics { role = Role.Button }, + modifier = Modifier.size(26.dp), paintable = Icons.AutoMirrored.Outlined.VolumeUp.asPaintable(), contentDescription = stringResource(id = R.string.horologist_volume_screen_volume_up_content_description), ) @@ -270,7 +264,7 @@ public object VolumeScreenDefaults { @Composable public fun DecreaseIcon() { Icon( - modifier = Modifier.size(26.dp).semantics { role = Role.Button }, + modifier = Modifier.size(26.dp), paintable = Icons.AutoMirrored.Outlined.VolumeDown.asPaintable(), contentDescription = stringResource(id = R.string.horologist_volume_screen_volume_down_content_description), )