Add "View avatar" content description to all clickable Avatar that will open the avatar preview. (#4948)
* Add "View avatar" content description to all clickable Avatar that will open the avatar preview. * Improve accessibility of avatar images.
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
<string name="a11y_jump_to_bottom">"Jump to bottom"</string>
|
||||
<string name="a11y_notifications_mentions_only">"Mentions only"</string>
|
||||
<string name="a11y_notifications_muted">"Muted"</string>
|
||||
<string name="a11y_other_user_avatar">"Other user avatar"</string>
|
||||
<string name="a11y_page_n">"Page %1$d"</string>
|
||||
<string name="a11y_pause">"Pause"</string>
|
||||
<string name="a11y_paused_voice_message">"Voice message, duration: %1$s, current position: %2$s"</string>
|
||||
@@ -30,15 +31,18 @@
|
||||
<string name="a11y_read_receipts_tap_to_show_all">"Tap to show all"</string>
|
||||
<string name="a11y_remove_reaction">"Remove reaction: %1$s"</string>
|
||||
<string name="a11y_remove_reaction_with">"Remove reaction with %1$s"</string>
|
||||
<string name="a11y_room_avatar">"Room avatar"</string>
|
||||
<string name="a11y_send_files">"Send files"</string>
|
||||
<string name="a11y_show_password">"Show password"</string>
|
||||
<string name="a11y_start_call">"Start a call"</string>
|
||||
<string name="a11y_user_avatar">"User avatar"</string>
|
||||
<string name="a11y_user_menu">"User menu"</string>
|
||||
<string name="a11y_view_avatar">"View avatar"</string>
|
||||
<string name="a11y_view_details">"View details"</string>
|
||||
<string name="a11y_voice_message">"Voice message, duration: %1$s"</string>
|
||||
<string name="a11y_voice_message_record">"Record voice message."</string>
|
||||
<string name="a11y_voice_message_stop_recording">"Stop recording"</string>
|
||||
<string name="a11y_your_avatar">"Your avatar"</string>
|
||||
<string name="action_accept">"Accept"</string>
|
||||
<string name="action_add_caption">"Add caption"</string>
|
||||
<string name="action_add_to_timeline">"Add to timeline"</string>
|
||||
@@ -137,6 +141,7 @@
|
||||
<string name="action_tap_for_options">"Tap for options"</string>
|
||||
<string name="action_try_again">"Try again"</string>
|
||||
<string name="action_unpin">"Unpin"</string>
|
||||
<string name="action_view">"View"</string>
|
||||
<string name="action_view_in_timeline">"View in timeline"</string>
|
||||
<string name="action_view_source">"View source"</string>
|
||||
<string name="action_yes">"Yes"</string>
|
||||
|
||||
Reference in New Issue
Block a user