Fix accessibility: remove TalkBack reverseLayout workaround (#6392)

The LazyColumn was using reverseLayout=false when TalkBack was active to work
around a Compose accessibility traversal bug. This placed new messages at the
top of the screen, distorting the reading order for TalkBack users.

With Compose BOM 2026.06.00, reverseLayout=true now works correctly with
TalkBack traversal, so remove both workarounds:
- Always use reverseLayout=true in TimelineView
- Remove the .reversed() for expanded groups in TimelineItemGroupedEventsRow

Note: this change should be manually tested with TalkBack to verify correct
traversal order (oldest messages first, newest last).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benoit Marty
2026-07-01 17:55:36 +02:00
parent 58b3830187
commit 54d452c364
2 changed files with 2 additions and 13 deletions
@@ -77,7 +77,6 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.TestTags
import io.element.android.libraries.testtags.testTag import io.element.android.libraries.testtags.testTag
import io.element.android.libraries.ui.strings.CommonStrings import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
import io.element.android.wysiwyg.link.Link import io.element.android.wysiwyg.link.Link
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.collectLatest
@@ -128,9 +127,6 @@ fun TimelineView(
val context = LocalContext.current val context = LocalContext.current
val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard) val toastMessage = stringResource(CommonStrings.common_copied_to_clipboard)
val view = LocalView.current val view = LocalView.current
// Disable reverse layout when TalkBack is enabled to avoid incorrect ordering issues seen in the current Compose UI version
val useReverseLayout = !isTalkbackActive()
fun inReplyToClick(eventId: EventId) { fun inReplyToClick(eventId: EventId) {
state.eventSink(TimelineEvent.FocusOnEvent(eventId)) state.eventSink(TimelineEvent.FocusOnEvent(eventId))
} }
@@ -158,7 +154,7 @@ fun TimelineView(
.nestedScroll(nestedScrollConnection) .nestedScroll(nestedScrollConnection)
.testTag(TestTags.timeline), .testTag(TestTags.timeline),
state = lazyListState, state = lazyListState,
reverseLayout = useReverseLayout, reverseLayout = true,
contentPadding = PaddingValues(top = 64.dp, bottom = 8.dp), contentPadding = PaddingValues(top = 64.dp, bottom = 8.dp),
) { ) {
items( items(
@@ -34,7 +34,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
import io.element.android.wysiwyg.link.Link import io.element.android.wysiwyg.link.Link
@Composable @Composable
@@ -158,13 +157,7 @@ private fun TimelineItemGroupedEventsRowContent(
) )
if (isExpanded) { if (isExpanded) {
Column { Column {
timelineItem.events.let { timelineItem.events.forEach { subGroupEvent ->
if (isTalkbackActive()) {
it.reversed()
} else {
it
}
}.forEach { subGroupEvent ->
TimelineItemRow( TimelineItemRow(
timelineMode = timelineMode, timelineMode = timelineMode,
timelineItem = subGroupEvent, timelineItem = subGroupEvent,