diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
index 02a6a637e3..c60a78dd22 100644
--- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
+++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt
@@ -58,6 +58,7 @@ import io.element.android.libraries.designsystem.components.button.MainActionBut
import io.element.android.libraries.designsystem.components.list.ListItemContent
import io.element.android.libraries.designsystem.components.preferences.PreferenceCategory
import io.element.android.libraries.designsystem.components.preferences.PreferenceSwitch
+import io.element.android.libraries.designsystem.modifiers.a11yClickLabel
import io.element.android.libraries.designsystem.modifiers.niceClickable
import io.element.android.libraries.designsystem.preview.ElementPreviewDark
import io.element.android.libraries.designsystem.preview.ElementPreviewLight
@@ -400,6 +401,7 @@ private fun RoomHeaderSection(
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ val actionView = stringResource(CommonStrings.action_view)
Avatar(
avatarData = AvatarData(roomId.value, roomName, avatarUrl, AvatarSize.RoomHeader),
avatarType = AvatarType.Room(
@@ -408,9 +410,11 @@ private fun RoomHeaderSection(
}.toPersistentList(),
isTombstoned = isTombstoned,
),
+ contentDescription = avatarUrl?.let { stringResource(CommonStrings.a11y_room_avatar) },
modifier = Modifier
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
.testTag(TestTags.roomDetailAvatar)
+ .a11yClickLabel(avatarUrl?.let { actionView })
)
TitleAndSubtitle(
title = roomName,
diff --git a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt
index 2732e770f4..47bb9cbdc5 100644
--- a/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt
+++ b/features/userprofile/shared/src/main/kotlin/io/element/android/features/userprofile/shared/UserProfileHeaderSection.kt
@@ -31,6 +31,7 @@ import io.element.android.libraries.designsystem.components.avatar.Avatar
import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.avatar.AvatarType
+import io.element.android.libraries.designsystem.modifiers.a11yClickLabel
import io.element.android.libraries.designsystem.modifiers.niceClickable
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@@ -60,13 +61,16 @@ fun UserProfileHeaderSection(
.padding(horizontal = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
+ val actionView = stringResource(CommonStrings.action_view)
Avatar(
avatarData = AvatarData(userId.value, userName, avatarUrl, AvatarSize.UserHeader),
avatarType = AvatarType.User,
+ contentDescription = avatarUrl?.let { stringResource(CommonStrings.a11y_user_avatar) },
modifier = Modifier
.clip(CircleShape)
.clickable(enabled = avatarUrl != null) { openAvatarPreview(avatarUrl!!) }
.testTag(TestTags.memberDetailAvatar)
+ .a11yClickLabel(avatarUrl?.let { actionView })
)
Spacer(modifier = Modifier.height(24.dp))
if (userName != null) {
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt
index d0134fca78..56071de747 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/avatar/DmAvatars.kt
@@ -23,13 +23,16 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
+import io.element.android.libraries.designsystem.modifiers.a11yClickLabel
import io.element.android.libraries.designsystem.preview.ElementThemedPreview
import io.element.android.libraries.designsystem.preview.PreviewGroup
import io.element.android.libraries.designsystem.text.toPx
import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag
+import io.element.android.libraries.ui.strings.CommonStrings
/** Ratio between the box size (120 on Figma) and the avatar size (75 on Figma). */
private const val SIZE_RATIO = 1.6f
@@ -49,6 +52,7 @@ fun DmAvatars(
val boxSizePx = boxSize.toPx()
val otherAvatarRadius = otherUserAvatarData.size.dp.toPx() / 2
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
+ val actionView = stringResource(CommonStrings.action_view)
Box(
modifier = modifier.size(boxSize),
) {
@@ -56,6 +60,7 @@ fun DmAvatars(
Avatar(
avatarData = userAvatarData,
avatarType = AvatarType.User,
+ contentDescription = userAvatarData.url?.let { stringResource(CommonStrings.a11y_your_avatar) },
modifier = Modifier
.align(Alignment.BottomStart)
.graphicsLayer {
@@ -82,11 +87,13 @@ fun DmAvatars(
.clickable(enabled = userAvatarData.url != null) {
userAvatarData.url?.let { openAvatarPreview(it) }
}
+ .a11yClickLabel(userAvatarData.url?.let { actionView })
)
// Draw other user avatar
Avatar(
avatarData = otherUserAvatarData,
avatarType = AvatarType.User,
+ contentDescription = otherUserAvatarData.url?.let { stringResource(CommonStrings.a11y_other_user_avatar) },
modifier = Modifier
.align(Alignment.TopEnd)
.clip(CircleShape)
@@ -94,6 +101,7 @@ fun DmAvatars(
otherUserAvatarData.url?.let { openOtherAvatarPreview(it) }
}
.testTag(TestTags.memberDetailAvatar)
+ .a11yClickLabel(otherUserAvatarData.url?.let { actionView })
)
}
}
diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Clickable.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Clickable.kt
index 5789a4525d..753ad952a0 100644
--- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Clickable.kt
+++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/modifiers/Clickable.kt
@@ -12,6 +12,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
fun Modifier.clickableIfNotNull(onClick: (() -> Unit)? = null): Modifier = this.then(
@@ -29,3 +31,18 @@ fun Modifier.niceClickable(
.clickable { onClick() }
.padding(horizontal = 4.dp)
}
+
+fun Modifier.a11yClickLabel(
+ label: String?,
+): Modifier = then(
+ if (label != null) {
+ Modifier.semantics {
+ onClick(
+ label = label,
+ action = null,
+ )
+ }
+ } else {
+ Modifier
+ }
+)
diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml
index bf97f4c46b..41e87f83c5 100644
--- a/libraries/ui-strings/src/main/res/values/localazy.xml
+++ b/libraries/ui-strings/src/main/res/values/localazy.xml
@@ -12,6 +12,7 @@
"Jump to bottom"
"Mentions only"
"Muted"
+ "Other user avatar"
"Page %1$d"
"Pause"
"Voice message, duration: %1$s, current position: %2$s"
@@ -30,15 +31,18 @@
"Tap to show all"
"Remove reaction: %1$s"
"Remove reaction with %1$s"
+ "Room avatar"
"Send files"
"Show password"
"Start a call"
+ "User avatar"
"User menu"
"View avatar"
"View details"
"Voice message, duration: %1$s"
"Record voice message."
"Stop recording"
+ "Your avatar"
"Accept"
"Add caption"
"Add to timeline"
@@ -137,6 +141,7 @@
"Tap for options"
"Try again"
"Unpin"
+ "View"
"View in timeline"
"View source"
"Yes"