mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge pull request #7138 from element-hq/fix/a11y-reading-order
Fix timeline reading order distorted when TalkBack is active
This commit is contained in:
+7
-13
@@ -103,7 +103,6 @@ import io.element.android.libraries.testtags.TestTag
|
|||||||
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.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@@ -165,9 +164,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))
|
||||||
}
|
}
|
||||||
@@ -195,7 +191,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(
|
||||||
@@ -255,14 +251,12 @@ fun TimelineView(
|
|||||||
onFocusOnEvent = ::onFocusOnEvent,
|
onFocusOnEvent = ::onFocusOnEvent,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (useReverseLayout) {
|
FloatingDateBadgeOverlay(
|
||||||
FloatingDateBadgeOverlay(
|
lazyListState = lazyListState,
|
||||||
lazyListState = lazyListState,
|
timelineItems = state.timelineItems,
|
||||||
timelineItems = state.timelineItems,
|
isLive = state.isLive,
|
||||||
isLive = state.isLive,
|
topOffset = floatingDateTopOffset,
|
||||||
topOffset = floatingDateTopOffset,
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -27,6 +27,8 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
|
import androidx.compose.ui.semantics.hideFromAccessibility
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
@@ -102,7 +104,10 @@ internal fun BoxScope.FloatingDateBadgeOverlay(
|
|||||||
visible = showBadge,
|
visible = showBadge,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopCenter)
|
.align(Alignment.TopCenter)
|
||||||
.padding(top = 8.dp + topOffset),
|
.padding(top = 8.dp + topOffset)
|
||||||
|
.clearAndSetSemantics {
|
||||||
|
hideFromAccessibility()
|
||||||
|
},
|
||||||
enter = fadeIn(animationSpec = tween(150)),
|
enter = fadeIn(animationSpec = tween(150)),
|
||||||
exit = fadeOut(animationSpec = tween(300)),
|
exit = fadeOut(animationSpec = tween(300)),
|
||||||
) {
|
) {
|
||||||
|
|||||||
+1
-8
@@ -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
|
||||||
@@ -160,13 +159,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,
|
||||||
|
|||||||
+9
-5
@@ -61,10 +61,7 @@ fun TimelineItemReadReceiptView(
|
|||||||
ReadReceiptsRow(modifier = modifier) {
|
ReadReceiptsRow(modifier = modifier) {
|
||||||
ReadReceiptsAvatars(
|
ReadReceiptsAvatars(
|
||||||
receipts = state.receipts,
|
receipts = state.receipts,
|
||||||
modifier = Modifier
|
onClick = onReadReceiptsClick,
|
||||||
.clip(RoundedCornerShape(4.dp))
|
|
||||||
.clickable(onClick = onReadReceiptsClick)
|
|
||||||
.padding(2.dp)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -124,7 +121,8 @@ private fun ReadReceiptsRow(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun ReadReceiptsAvatars(
|
private fun ReadReceiptsAvatars(
|
||||||
receipts: ImmutableList<ReadReceiptData>,
|
receipts: ImmutableList<ReadReceiptData>,
|
||||||
modifier: Modifier = Modifier
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val avatarSize = AvatarSize.TimelineReadReceipt.dp
|
val avatarSize = AvatarSize.TimelineReadReceipt.dp
|
||||||
val avatarStrokeSize = 1.dp
|
val avatarStrokeSize = 1.dp
|
||||||
@@ -132,6 +130,12 @@ private fun ReadReceiptsAvatars(
|
|||||||
val receiptDescription = computeReceiptDescription(receipts)
|
val receiptDescription = computeReceiptDescription(receipts)
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
|
.clip(RoundedCornerShape(4.dp))
|
||||||
|
.clickable(
|
||||||
|
onClick = onClick,
|
||||||
|
onClickLabel = stringResource(id = CommonStrings.a11y_read_receipts_tap_to_show_all_android),
|
||||||
|
)
|
||||||
|
.padding(2.dp)
|
||||||
.clearAndSetSemantics {
|
.clearAndSetSemantics {
|
||||||
testTag = TestTags.messageReadReceipts.value
|
testTag = TestTags.messageReadReceipts.value
|
||||||
contentDescription = receiptDescription
|
contentDescription = receiptDescription
|
||||||
|
|||||||
@@ -44,13 +44,14 @@
|
|||||||
<string name="a11y_qr_code">"QR Code"</string>
|
<string name="a11y_qr_code">"QR Code"</string>
|
||||||
<string name="a11y_react_with">"React with %1$s"</string>
|
<string name="a11y_react_with">"React with %1$s"</string>
|
||||||
<string name="a11y_react_with_other_emojis">"React with other emojis"</string>
|
<string name="a11y_react_with_other_emojis">"React with other emojis"</string>
|
||||||
<string name="a11y_read_receipts_multiple">"Read by %1$s and %2$s"</string>
|
<string name="a11y_read_receipts_multiple">"Previous message read by %1$s and %2$s"</string>
|
||||||
<plurals name="a11y_read_receipts_multiple_with_others">
|
<plurals name="a11y_read_receipts_multiple_with_others">
|
||||||
<item quantity="one">"Read by %1$s and %2$d other"</item>
|
<item quantity="one">"Previous message read by %1$s and %2$d other"</item>
|
||||||
<item quantity="other">"Read by %1$s and %2$d others"</item>
|
<item quantity="other">"Previous message read by %1$s and %2$d others"</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="a11y_read_receipts_single">"Read by %1$s"</string>
|
<string name="a11y_read_receipts_single">"Previous message read by %1$s"</string>
|
||||||
<string name="a11y_read_receipts_tap_to_show_all">"Tap to show all"</string>
|
<string name="a11y_read_receipts_tap_to_show_all">"Tap to show all"</string>
|
||||||
|
<string name="a11y_read_receipts_tap_to_show_all_android">"show all read receipts"</string>
|
||||||
<string name="a11y_remove_reaction">"Remove reaction: %1$s"</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_remove_reaction_with">"Remove reaction with %1$s"</string>
|
||||||
<string name="a11y_room_avatar">"Room avatar"</string>
|
<string name="a11y_room_avatar">"Room avatar"</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user