From ace078f12ef39285164ec6a7fa958dfb601f1c38 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Wed, 27 Aug 2025 17:14:59 +0200 Subject: [PATCH] [a11y] Add content descriptions to room list item indicators (#5236) * [a11y] Add content descriptions to room list item indicators. These can now be read aloud as 'ongoing call', 'new messages', 'new mentions'. * Add `contentDescription` to `UnreadIndicatorAtom` as an optional value * Make the 'ongoing call', 'new messages', etc. indicators be read aloud before the latest event of the room summary --------- Co-authored-by: ElementBot --- .../home/impl/components/RoomSummaryRow.kt | 17 ++++++++++++----- .../atomic/atoms/UnreadIndicatorAtom.kt | 6 ++++++ .../features.home.impl_HomeViewA11y_en.png | 4 ++-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt index a065da16e5..75ec727055 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/components/RoomSummaryRow.kt @@ -35,6 +35,7 @@ import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import io.element.android.compound.theme.ElementTheme import io.element.android.compound.tokens.generated.CompoundIcons import io.element.android.features.home.impl.R @@ -285,9 +286,13 @@ private fun MessagePreviewAndIndicatorRow( maxLines = 2, overflow = TextOverflow.Ellipsis ) + // Call and unread Row( - modifier = Modifier.height(16.dp), + modifier = Modifier + .height(16.dp) + // Used to force this line to be read aloud earlier than the latest event when using Talkback + .zIndex(-1f), horizontalArrangement = Arrangement.spacedBy(8.dp), verticalAlignment = Alignment.CenterVertically, ) { @@ -303,8 +308,10 @@ private fun MessagePreviewAndIndicatorRow( MentionIndicatorAtom() } if (room.hasNewContent) { + val contentDescription = stringResource(CommonStrings.a11y_notifications_new_messages) UnreadIndicatorAtom( - color = tint + color = tint, + contentDescription = contentDescription, ) } } @@ -371,7 +378,7 @@ private fun OnGoingCallIcon( Icon( modifier = Modifier.size(16.dp), imageVector = CompoundIcons.VideoCallSolid(), - contentDescription = null, + contentDescription = stringResource(CommonStrings.a11y_notifications_ongoing_call), tint = color, ) } @@ -380,7 +387,7 @@ private fun OnGoingCallIcon( private fun NotificationOffIndicatorAtom() { Icon( modifier = Modifier.size(16.dp), - contentDescription = null, + contentDescription = stringResource(CommonStrings.a11y_notifications_muted), imageVector = CompoundIcons.NotificationsOffSolid(), tint = ElementTheme.colors.iconQuaternary, ) @@ -390,7 +397,7 @@ private fun NotificationOffIndicatorAtom() { private fun MentionIndicatorAtom() { Icon( modifier = Modifier.size(16.dp), - contentDescription = null, + contentDescription = stringResource(CommonStrings.a11y_notifications_new_mentions), imageVector = CompoundIcons.Mention(), tint = ElementTheme.colors.unreadIndicator, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt index 8351226f85..c9aa5901ec 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/atomic/atoms/UnreadIndicatorAtom.kt @@ -15,6 +15,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import io.element.android.compound.theme.ElementTheme @@ -28,9 +30,13 @@ fun UnreadIndicatorAtom( size: Dp = 12.dp, color: Color = ElementTheme.colors.unreadIndicator, isVisible: Boolean = true, + contentDescription: String? = null, ) { Box( modifier = modifier + .semantics { + contentDescription?.let { this.contentDescription = it } + } .size(size) .clip(CircleShape) .background(if (isVisible) color else Color.Transparent) diff --git a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png index f34d4bae74..dc6b4dabbf 100644 --- a/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png +++ b/tests/uitests/src/test/snapshots/images/features.home.impl_HomeViewA11y_en.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21c09cac237b2b4823dbb945161c6d6d574dc2c8ffb37088696d09736d8e782a -size 124636 +oid sha256:a666f932d1a595763b3c88b31ffbe3d195ec0d986ca5f2c0173e8e25a6115317 +size 126085