Fix accessibility: message composer label and 'Seen by' button (#6390, #6393)

- MarkdownTextInput: set contentDescription on MarkdownEditText so TalkBack
  announces a persistent label even when the field has content (fixes #6390)
- TimelineItemReadReceiptView: remove hideFromAccessibility() so the 'Seen by'
  button is exposed to TalkBack; use semantics (not clearAndSetSemantics) to
  preserve click action, and add Role.Button (fixes #6393)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benoit Marty
2026-07-01 17:49:32 +02:00
parent 58b3830187
commit 79f1423a30
2 changed files with 9 additions and 11 deletions
@@ -25,9 +25,10 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.Role
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.hideFromAccessibility import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -57,18 +58,12 @@ fun TimelineItemReadReceiptView(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
if (state.receipts.isNotEmpty()) { if (state.receipts.isNotEmpty()) {
ReadReceiptsRow( ReadReceiptsRow(modifier = modifier) {
modifier = modifier.clearAndSetSemantics {
hideFromAccessibility()
}
) {
ReadReceiptsAvatars( ReadReceiptsAvatars(
receipts = state.receipts, receipts = state.receipts,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(4.dp)) .clip(RoundedCornerShape(4.dp))
.clickable { .clickable { onReadReceiptsClick() }
onReadReceiptsClick()
}
.padding(2.dp) .padding(2.dp)
) )
} }
@@ -137,9 +132,10 @@ private fun ReadReceiptsAvatars(
val receiptDescription = computeReceiptDescription(receipts) val receiptDescription = computeReceiptDescription(receipts)
Row( Row(
modifier = modifier modifier = modifier
.clearAndSetSemantics { .semantics {
testTag = TestTags.messageReadReceipts.value testTag = TestTags.messageReadReceipts.value
contentDescription = receiptDescription contentDescription = receiptDescription
role = Role.Button
}, },
horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize), horizontalArrangement = Arrangement.spacedBy(4.dp - avatarStrokeSize),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@@ -93,6 +93,7 @@ fun MarkdownTextInput(
setText(text) setText(text)
setHint(placeholder) setHint(placeholder)
setHintTextColor(ColorStateList.valueOf(placeholderColor.toArgb())) setHintTextColor(ColorStateList.valueOf(placeholderColor.toArgb()))
contentDescription = placeholder
inputType = InputType.TYPE_CLASS_TEXT or inputType = InputType.TYPE_CLASS_TEXT or
InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or
InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_TEXT_FLAG_MULTI_LINE or
@@ -129,6 +130,7 @@ fun MarkdownTextInput(
} }
}, },
update = { editText -> update = { editText ->
editText.contentDescription = placeholder
editText.applyStyleInCompose(richTextEditorStyle) editText.applyStyleInCompose(richTextEditorStyle)
val text = state.text.value() val text = state.text.value()
mentionSpanUpdater.updateMentionSpans(text) mentionSpanUpdater.updateMentionSpans(text)