diff --git a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt index 942e924820..f67338ef21 100644 --- a/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt +++ b/features/home/impl/src/main/kotlin/io/element/android/features/home/impl/search/RoomListSearchView.kt @@ -33,6 +33,8 @@ import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.compound.tokens.generated.CompoundIcons @@ -112,10 +114,12 @@ private fun RoomListSearchContent( title = { // The stateSaver will keep the selection state when returning to this UI val focusRequester = remember { FocusRequester() } + val searchLabel = stringResource(CommonStrings.action_search) FilledTextField( modifier = Modifier .fillMaxWidth() - .focusRequester(focusRequester), + .focusRequester(focusRequester) + .semantics { contentDescription = searchLabel }, state = state.query, lineLimits = TextFieldLineLimits.SingleLine, colors = TextFieldDefaults.colors( @@ -132,7 +136,7 @@ private fun RoomListSearchContent( IconButton(onClick = { state.eventSink(RoomListSearchEvent.ClearQuery) }) { Icon( imageVector = CompoundIcons.Close(), - contentDescription = stringResource(CommonStrings.action_cancel) + contentDescription = stringResource(CommonStrings.a11y_clear_search_field) ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt index 037d9ea45d..3ccf32d422 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/receipt/TimelineItemReadReceiptView.kt @@ -25,9 +25,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription -import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.testTag import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp @@ -57,18 +58,12 @@ fun TimelineItemReadReceiptView( modifier: Modifier = Modifier, ) { if (state.receipts.isNotEmpty()) { - ReadReceiptsRow( - modifier = modifier.clearAndSetSemantics { - hideFromAccessibility() - } - ) { + ReadReceiptsRow(modifier = modifier) { ReadReceiptsAvatars( receipts = state.receipts, modifier = Modifier .clip(RoundedCornerShape(4.dp)) - .clickable { - onReadReceiptsClick() - } + .clickable(onClick = onReadReceiptsClick) .padding(2.dp) ) } @@ -140,6 +135,7 @@ private fun ReadReceiptsAvatars( .clearAndSetSemantics { testTag = TestTags.messageReadReceipts.value contentDescription = receiptDescription + role = Role.Button }, horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize), verticalAlignment = Alignment.CenterVertically, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt index 319aebe33e..f7564aec31 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/DefaultLocalMediaRenderer.kt @@ -40,6 +40,7 @@ class DefaultLocalMediaRenderer( localMediaViewState = localMediaViewState, textFileViewer = textFileViewer, audioFocus = audioFocus, + forPreview = true, onClick = {}, onOpenWith = null, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt index c19b1a24c0..01b44f1246 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/LocalMediaView.kt @@ -33,6 +33,7 @@ fun LocalMediaView( onClick: () -> Unit, onOpenWith: (() -> Unit)?, textFileViewer: TextFileViewer, + forPreview: Boolean, modifier: Modifier = Modifier, isDisplayed: Boolean = true, isUserSelected: Boolean = false, @@ -45,6 +46,7 @@ fun LocalMediaView( localMediaViewState = localMediaViewState, localMedia = localMedia, modifier = modifier, + forPreview = forPreview, onClick = onClick, ) mimeType.isMimeTypeVideo() -> MediaVideoView( @@ -54,6 +56,7 @@ fun LocalMediaView( localMedia = localMedia, autoplay = isUserSelected, audioFocus = audioFocus, + forPreview = forPreview, modifier = modifier, ) mimeType == MimeTypes.PlainText -> TextFileView( diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/image/MediaImageView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/image/MediaImageView.kt index 4c73dd215d..c935b17b85 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/image/MediaImageView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/image/MediaImageView.kt @@ -30,6 +30,7 @@ import me.saket.telephoto.zoomable.rememberZoomableImageState fun MediaImageView( localMediaViewState: LocalMediaViewState, localMedia: LocalMedia?, + forPreview: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier, ) { @@ -42,11 +43,16 @@ fun MediaImageView( } else { val zoomableImageState = rememberZoomableImageState(localMediaViewState.zoomableState) localMediaViewState.isReady = zoomableImageState.isImageDisplayed + val imageDescription = if (forPreview) { + stringResource(CommonStrings.a11y_photo_preview) + } else { + stringResource(CommonStrings.common_image) + } ZoomableAsyncImage( modifier = modifier, state = zoomableImageState, model = localMedia?.uri, - contentDescription = stringResource(id = CommonStrings.common_image), + contentDescription = imageDescription, contentScale = ContentScale.Fit, onClick = { onClick() } ) @@ -60,6 +66,7 @@ internal fun MediaImageViewPreview() = ElementPreview { modifier = Modifier.fillMaxSize(), localMediaViewState = rememberLocalMediaViewState(), localMedia = null, + forPreview = false, onClick = {}, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt index 21279a598e..d6484cd5a4 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt @@ -30,6 +30,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.res.stringResource import androidx.compose.ui.viewinterop.AndroidView import androidx.lifecycle.Lifecycle import androidx.media3.common.MediaItem @@ -60,6 +61,7 @@ import io.element.android.libraries.mediaviewer.impl.local.player.rememberExoPla import io.element.android.libraries.mediaviewer.impl.local.player.seekToEnsurePlaying import io.element.android.libraries.mediaviewer.impl.local.player.togglePlay import io.element.android.libraries.mediaviewer.impl.local.rememberLocalMediaViewState +import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.utils.a11y.isTalkbackActive import kotlinx.coroutines.delay import me.saket.telephoto.zoomable.zoomable @@ -75,6 +77,7 @@ fun MediaVideoView( localMedia: LocalMedia?, autoplay: Boolean, audioFocus: AudioFocus?, + forPreview: Boolean, modifier: Modifier = Modifier, ) { val exoPlayer = rememberExoPlayer(forAudioOnly = false) @@ -86,6 +89,7 @@ fun MediaVideoView( localMedia = localMedia, autoplay = autoplay, audioFocus = audioFocus, + forPreview = forPreview, modifier = modifier, ) } @@ -100,6 +104,7 @@ private fun ExoPlayerMediaVideoView( localMedia: LocalMedia?, autoplay: Boolean, audioFocus: AudioFocus?, + forPreview: Boolean, modifier: Modifier = Modifier, ) { var mediaPlayerControllerState: MediaPlayerControllerState by remember { @@ -218,6 +223,11 @@ private fun ExoPlayerMediaVideoView( text = "A Video Player will render here", ) } else { + val videoDescription = if (forPreview) { + stringResource(CommonStrings.a11y_video_preview) + } else { + stringResource(CommonStrings.common_video) + } AndroidView( modifier = Modifier .fillMaxSize() @@ -236,6 +246,7 @@ private fun ExoPlayerMediaVideoView( resizeMode = AspectRatioFrameLayout.RESIZE_MODE_FIT layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) useController = false + contentDescription = videoDescription } }, onRelease = { playerView -> @@ -355,5 +366,6 @@ internal fun MediaVideoViewPreview() = ElementPreview { localMedia = null, audioFocus = null, autoplay = false, + forPreview = false, ) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt index c4d442338a..c981132b48 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/viewer/MediaViewerView.kt @@ -396,6 +396,7 @@ private fun MediaViewerPage( onOpenWith = onOpenWith, isUserSelected = isUserSelected, audioFocus = audioFocus, + forPreview = false, ) if (showThumbnail) { ThumbnailView( diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt index bd6944d603..064ef03106 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/markdown/MarkdownTextInput.kt @@ -93,6 +93,7 @@ fun MarkdownTextInput( setText(text) setHint(placeholder) setHintTextColor(ColorStateList.valueOf(placeholderColor.toArgb())) + contentDescription = placeholder inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or InputType.TYPE_TEXT_FLAG_MULTI_LINE or @@ -129,6 +130,7 @@ fun MarkdownTextInput( } }, update = { editText -> + editText.contentDescription = placeholder editText.applyStyleInCompose(richTextEditorStyle) val text = state.text.value() mentionSpanUpdater.updateMentionSpans(text) diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index f477af40d8..4885c38ef3 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -4,6 +4,7 @@ "Add reaction: %1$s" "Address" "Avatar" + "Clear search field" "Minimise message text field" "Decrease votes allowed per person" "Increase votes allowed per person"