Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.isTraversalGroup
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.text.style.TextAlign
Expand Down Expand Up @@ -168,7 +169,13 @@ public fun MediaAttachmentContent(
stringResource(UiCommonR.string.stream_ui_message_list_semantics_message_attachments, attachments.size)
Row(
modifier = Modifier
.semantics { this.contentDescription = description }
.semantics {
this.contentDescription = description
// Keep the grid's tiles together and in order during TalkBack traversal
// so a swipe walks every tile before leaving the grid, instead of letting
// the surrounding list pull focus across rows mid-grid.
isTraversalGroup = true
}
.padding(MessageStyling.messageSectionPadding),
horizontalArrangement = Arrangement.spacedBy(gridSpacing),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,14 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.clearAndSetSemantics
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService
import coil3.ColorImage
import coil3.compose.LocalAsyncImagePreviewHandler
import io.getstream.chat.android.client.utils.attachment.isGiphy
import io.getstream.chat.android.compose.R
import io.getstream.chat.android.compose.state.messages.attachments.AttachmentState
import io.getstream.chat.android.compose.ui.components.button.StreamButtonStyleDefaults
Expand Down Expand Up @@ -96,6 +98,20 @@ public fun GiphyMessageContent(
val cancelledAnnouncement = stringResource(R.string.stream_compose_message_list_giphy_cancelled)
val shuffledAnnouncement = stringResource(R.string.stream_compose_message_list_giphy_shuffled)

// The focused preview is announced as one TalkBack stop. The reading order intentionally leads
// with the Giphy context, which differs from the visual top-to-bottom order (the "only visible
// to you" banner sits on top). That order spans the banner and the overridable
// GiphyAttachmentContent, so no single leaf can own it: compose the description here and clear
// the children's semantics below. Alt text is sourced exactly as GiphyAttachmentContent does.
val giphyPreviewLabel = stringResource(R.string.stream_compose_giphy_preview_label)
val onlyVisibleToYou = stringResource(R.string.stream_compose_only_visible_to_you)
val giphyAltText = message.attachments
.firstOrNull(Attachment::isGiphy)
?.title
?.takeIf(String::isNotBlank)
val previewDescription = listOfNotNull(giphyPreviewLabel, giphyAltText, onlyVisibleToYou)
.joinToString(separator = ", ")

val isTouchExplorationEnabled = rememberIsTouchExplorationEnabled()
val previewFocusRequester = remember { FocusRequester() }
// Track whether the preview has already requested focus so that a LazyColumn dispose +
Expand All @@ -120,7 +136,7 @@ public fun GiphyMessageContent(
.applyIf(isTouchExplorationEnabled) {
focusRequester(previewFocusRequester).focusable()
}
.semantics(mergeDescendants = true) {},
.clearAndSetSemantics { contentDescription = previewDescription },
Comment thread
gpunto marked this conversation as resolved.
) {
Row(
modifier = Modifier
Expand All @@ -136,7 +152,7 @@ public fun GiphyMessageContent(
tint = colors.chatTextOutgoing,
)
Text(
text = stringResource(R.string.stream_compose_only_visible_to_you),
text = onlyVisibleToYou,
style = ChatTheme.typography.captionEmphasis,
color = colors.chatTextOutgoing,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ internal fun MessageComposerQuotedMessage(
message = message,
currentUser = currentUser,
replyMessage = null,
// No click handler on the composer quoted preview; merge so its sender + text announce as one stop.
modifier = Modifier.semantics(mergeDescendants = true) {},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ internal fun PollAnswersItem(
verticalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs),
) {
Column(
// No click handler; merge so the answer text + author row announce as one TalkBack stop.
modifier = Modifier.semantics(mergeDescendants = true) {},
verticalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ private fun Content(
Row(
modifier = Modifier
.fillMaxWidth()
// No click handler; merge so the winner badge + vote count announce as one stop.
.semantics(mergeDescendants = true) {},
horizontalArrangement = Arrangement.End,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ internal fun PollVoteItem(
val borderSize = 2.dp

Row(
// No click handler; merge so the voter avatar + name announce as one TalkBack stop.
modifier = modifier.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(StreamTokens.spacingSm),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,7 @@ private fun PollSwitchHeader(
value = enabled,
role = Role.Switch,
onValueChange = onCheckedChange,
)
.semantics(mergeDescendants = true) {},
),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
Expand Down Expand Up @@ -230,8 +229,7 @@ private fun LimitVotesPerPerson(
value = enabled,
role = Role.Switch,
onValueChange = onCheckedChange,
)
.semantics(mergeDescendants = true) {},
),
verticalAlignment = Alignment.CenterVertically,
) {
Column(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,17 @@ internal fun MessageComposerAudioRecordingLockedContent(
modifier = RecordingBarModifier
.focusRequester(rowFocusRequester)
.focusable()
.semantics(mergeDescendants = true) { contentDescription = rowLabel },
// Merge the row into a single TalkBack stop. The label is owned by the leading icon
// leaf (below) rather than set here, so children announce in visual order
// (label, duration, progress) and a new child appends in its slot instead of
// silently shifting a parent-level description.
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
) {
Box(modifier = Modifier.size(48.dp), contentAlignment = Alignment.Center) {
Icon(
painter = painterResource(id = R.drawable.stream_design_ic_voice),
contentDescription = null,
contentDescription = rowLabel,
tint = ChatTheme.colors.accentError,
)
}
Expand Down Expand Up @@ -314,7 +318,10 @@ private fun RowScope.OverviewPlaybackRow(
.fillMaxHeight()
.focusRequester(focusRequester)
.focusable()
.semantics(mergeDescendants = true) { contentDescription = rowLabel },
// Merge the row into a single TalkBack stop. The label is owned by the waveform leaf
// (below) rather than set here, so the announce is composed from the children instead
// of a parent description that would concatenate with any future child.
.semantics(mergeDescendants = true) {},
verticalAlignment = Alignment.CenterVertically,
) {
val playbackInMs = (currentProgress * state.durationInMs).toInt()
Expand All @@ -330,6 +337,7 @@ private fun RowScope.OverviewPlaybackRow(
StaticWaveformSlider(
modifier = Modifier
.fillMaxSize()
.semantics { contentDescription = rowLabel }
.padding(start = StreamTokens.spacingMd, top = 8.dp, bottom = 8.dp),
waveformData = state.waveform,
progress = currentProgress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ private fun EditIndicatorCard(body: QuotedMessageBody) {
)
.padding(StreamTokens.spacingXs)
.height(IntrinsicSize.Min)
// No click handler here; merge so the divider + quoted-message text announce as one stop.
.semantics(mergeDescendants = true) {},
horizontalArrangement = Arrangement.spacedBy(StreamTokens.spacingXs),
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ internal fun DefaultChannelHeaderCenterContent(
onClickLabel = onHeaderTitleClickLabel,
role = Role.Button,
) { callback(channel) }
.semantics(mergeDescendants = true) {}
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ public fun MessageContainer(
true
}
}
// Deleted/uploading messages have no click handler; merge so the row's children
// (avatar, text, footer) announce as one TalkBack stop instead of separate ones.
else -> Modifier.semantics(mergeDescendants = true) {}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1105,6 +1105,11 @@ public interface ChatComponentFactory {
/**
* The default Giphy message content.
*
* For TalkBack, the focused preview is announced as a single merged node whose description is
* composed and owned here ("Giphy preview", alt text, "Only visible to you"). This clears the
* semantics of the inner [GiphyAttachmentContent], so to customize the preview's accessibility
* announcement, override this function rather than [GiphyAttachmentContent].
*
* @param params Parameters for this component.
*/
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<string name="stream_compose_flag_message_title">"Marcar mensaje"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Vista previa de Giphy"</string>
<string name="stream_compose_image_options">"Opciones de imagen"</string>
<string name="stream_compose_image_order">"%1$d de %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"Cancelar compartir"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<string name="stream_compose_flag_message_title">"Signaler le message"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Aperçu Giphy"</string>
<string name="stream_compose_image_options">"Options de l\'image"</string>
<string name="stream_compose_image_order">"%1$d sur %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"Annuler le partage"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
<string name="stream_compose_flag_message_title">"मैसेज चिह्नित करें"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Giphy पूर्वावलोकन"</string>
<string name="stream_compose_image_options">"इमेज के विकल्प"</string>
<string name="stream_compose_image_order">"%2$d में से %1$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"शेयरिंग रद्द करें"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@
<string name="stream_compose_flag_message_title">"Tandai Pesan"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Pratinjau Giphy"</string>
<string name="stream_compose_image_options">"Opsi gambar"</string>
<string name="stream_compose_image_order">"%1$d dari %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"Batalkan Berbagi"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
<string name="stream_compose_flag_message_title">"Segnala messaggio"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Anteprima Giphy"</string>
<string name="stream_compose_image_options">"Opzioni immagine"</string>
<string name="stream_compose_image_order">"%1$d di %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"Annulla condivisione"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<string name="stream_compose_flag_message_title">"メッセージを報告"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Giphyプレビュー"</string>
<string name="stream_compose_image_options">"画像オプション"</string>
<string name="stream_compose_image_order">"%1$d / %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"共有をキャンセル"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
<string name="stream_compose_flag_message_title">"메시지 신고"</string>
<string name="stream_compose_giphy_label">"GIPHY"</string>
<string name="stream_compose_giphy_preview">"Giphy"</string>
<string name="stream_compose_giphy_preview_label">"Giphy 미리보기"</string>
<string name="stream_compose_image_options">"이미지 옵션"</string>
<string name="stream_compose_image_order">"%1$d / %2$d"</string>
<string name="stream_compose_image_preview_cancel_sharing">"공유 취소"</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
<string name="stream_compose_video_preview">Video</string>
<string name="stream_compose_file_preview">File</string>
<string name="stream_compose_giphy_preview">Giphy</string>
<string name="stream_compose_giphy_preview_label">Giphy preview</string>
<string name="stream_compose_link_preview">Link</string>
<string name="stream_compose_message_deleted_preview">Message deleted</string>
<string name="stream_compose_no_messages_yet">No messages yet</string>
Expand Down
Loading