Improve how Talkback works with the timeline (#2404)
* Improve how Talkback works with the timeline * Fix interaction test by adding test tag * Disable reverse layout when a screen reader is enabled This messes up with the ordering, but fixes the scrolling when a screen reader is used.
This commit is contained in:
parent
2028604134
commit
8d543e19b1
8 changed files with 110 additions and 66 deletions
1
changelog.d/+improve-accessibility-in-timeline.bugfix
Normal file
1
changelog.d/+improve-accessibility-in-timeline.bugfix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Improve how Talkback works with the timeline. Sadly, it's still not 100% working, but there is some issue with the `LazyColumn` using `reverseLayout` that only Google can fix.
|
||||||
|
|
@ -18,9 +18,11 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.timeline
|
package io.element.android.features.messages.impl.timeline
|
||||||
|
|
||||||
|
import android.view.accessibility.AccessibilityManager
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.ExperimentalAnimationApi
|
import androidx.compose.animation.ExperimentalAnimationApi
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
|
|
@ -45,8 +47,8 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.rememberUpdatedState
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
|
||||||
import androidx.compose.ui.draw.rotate
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
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
|
||||||
|
|
@ -64,7 +66,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
|
||||||
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
import io.element.android.features.messages.impl.typing.TypingNotificationState
|
||||||
import io.element.android.features.messages.impl.typing.TypingNotificationView
|
import io.element.android.features.messages.impl.typing.TypingNotificationView
|
||||||
import io.element.android.features.messages.impl.typing.aTypingNotificationState
|
import io.element.android.features.messages.impl.typing.aTypingNotificationState
|
||||||
import io.element.android.libraries.designsystem.animation.alphaAnimation
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
|
import io.element.android.libraries.designsystem.theme.components.FloatingActionButton
|
||||||
|
|
@ -99,7 +100,13 @@ fun TimelineView(
|
||||||
state.eventSink(TimelineEvents.OnScrollFinished(firstVisibleIndex))
|
state.eventSink(TimelineEvents.OnScrollFinished(firstVisibleIndex))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val context = LocalContext.current
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
|
// Disable reverse layout when TalkBack is enabled to avoid incorrect ordering issues seen in the current Compose UI version
|
||||||
|
val useReverseLayout = remember {
|
||||||
|
val accessibilityManager = context.getSystemService(AccessibilityManager::class.java)
|
||||||
|
accessibilityManager.isTouchExplorationEnabled.not()
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
@Suppress("UNUSED_PARAMETER")
|
||||||
fun inReplyToClicked(eventId: EventId) {
|
fun inReplyToClicked(eventId: EventId) {
|
||||||
|
|
@ -107,67 +114,67 @@ fun TimelineView(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms
|
// Animate alpha when timeline is first displayed, to avoid flashes or glitching when viewing rooms
|
||||||
val alpha by alphaAnimation(label = "alpha for timeline")
|
AnimatedVisibility(visible = true, enter = fadeIn()) {
|
||||||
|
Box(modifier) {
|
||||||
Box(modifier = modifier.alpha(alpha)) {
|
LazyColumn(
|
||||||
LazyColumn(
|
modifier = Modifier.fillMaxSize(),
|
||||||
modifier = Modifier.fillMaxSize(),
|
state = lazyListState,
|
||||||
state = lazyListState,
|
reverseLayout = useReverseLayout,
|
||||||
reverseLayout = true,
|
contentPadding = PaddingValues(vertical = 8.dp),
|
||||||
contentPadding = PaddingValues(vertical = 8.dp),
|
) {
|
||||||
) {
|
item {
|
||||||
item {
|
TypingNotificationView(state = typingNotificationState)
|
||||||
TypingNotificationView(state = typingNotificationState)
|
}
|
||||||
}
|
items(
|
||||||
items(
|
items = state.timelineItems,
|
||||||
items = state.timelineItems,
|
contentType = { timelineItem -> timelineItem.contentType() },
|
||||||
contentType = { timelineItem -> timelineItem.contentType() },
|
key = { timelineItem -> timelineItem.identifier() },
|
||||||
key = { timelineItem -> timelineItem.identifier() },
|
) { timelineItem ->
|
||||||
) { timelineItem ->
|
TimelineItemRow(
|
||||||
TimelineItemRow(
|
timelineItem = timelineItem,
|
||||||
timelineItem = timelineItem,
|
timelineRoomInfo = state.timelineRoomInfo,
|
||||||
timelineRoomInfo = state.timelineRoomInfo,
|
renderReadReceipts = state.renderReadReceipts,
|
||||||
renderReadReceipts = state.renderReadReceipts,
|
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true &&
|
||||||
isLastOutgoingMessage = (timelineItem as? TimelineItem.Event)?.isMine == true &&
|
state.timelineItems.first().identifier() == timelineItem.identifier(),
|
||||||
state.timelineItems.first().identifier() == timelineItem.identifier(),
|
highlightedItem = state.highlightedEventId?.value,
|
||||||
highlightedItem = state.highlightedEventId?.value,
|
onClick = onMessageClicked,
|
||||||
onClick = onMessageClicked,
|
onLongClick = onMessageLongClicked,
|
||||||
onLongClick = onMessageLongClicked,
|
onUserDataClick = onUserDataClicked,
|
||||||
onUserDataClick = onUserDataClicked,
|
inReplyToClick = ::inReplyToClicked,
|
||||||
inReplyToClick = ::inReplyToClicked,
|
onReactionClick = onReactionClicked,
|
||||||
onReactionClick = onReactionClicked,
|
onReactionLongClick = onReactionLongClicked,
|
||||||
onReactionLongClick = onReactionLongClicked,
|
onMoreReactionsClick = onMoreReactionsClicked,
|
||||||
onMoreReactionsClick = onMoreReactionsClicked,
|
onReadReceiptClick = onReadReceiptClick,
|
||||||
onReadReceiptClick = onReadReceiptClick,
|
onTimestampClicked = onTimestampClicked,
|
||||||
onTimestampClicked = onTimestampClicked,
|
sessionState = state.sessionState,
|
||||||
sessionState = state.sessionState,
|
eventSink = state.eventSink,
|
||||||
eventSink = state.eventSink,
|
onSwipeToReply = onSwipeToReply,
|
||||||
onSwipeToReply = onSwipeToReply,
|
)
|
||||||
)
|
}
|
||||||
}
|
if (state.paginationState.hasMoreToLoadBackwards) {
|
||||||
if (state.paginationState.hasMoreToLoadBackwards) {
|
// Do not use key parameter to avoid wrong positioning
|
||||||
// Do not use key parameter to avoid wrong positioning
|
item(contentType = "TimelineLoadingMoreIndicator") {
|
||||||
item(contentType = "TimelineLoadingMoreIndicator") {
|
TimelineLoadingMoreIndicator()
|
||||||
TimelineLoadingMoreIndicator()
|
LaunchedEffect(Unit) {
|
||||||
LaunchedEffect(Unit) {
|
onReachedLoadMore()
|
||||||
onReachedLoadMore()
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (state.paginationState.beginningOfRoomReached && !state.timelineRoomInfo.isDirect) {
|
||||||
|
item(contentType = "BeginningOfRoomReached") {
|
||||||
|
TimelineItemRoomBeginningView(roomName = roomName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state.paginationState.beginningOfRoomReached && !state.timelineRoomInfo.isDirect) {
|
|
||||||
item(contentType = "BeginningOfRoomReached") {
|
|
||||||
TimelineItemRoomBeginningView(roomName = roomName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TimelineScrollHelper(
|
TimelineScrollHelper(
|
||||||
isTimelineEmpty = state.timelineItems.isEmpty(),
|
isTimelineEmpty = state.timelineItems.isEmpty(),
|
||||||
lazyListState = lazyListState,
|
lazyListState = lazyListState,
|
||||||
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
|
forceJumpToBottomVisibility = forceJumpToBottomVisibility,
|
||||||
newEventState = state.newEventState,
|
newEventState = state.newEventState,
|
||||||
onScrollFinishedAt = ::onScrollFinishedAt
|
onScrollFinishedAt = ::onScrollFinishedAt
|
||||||
)
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.draw.clipToBounds
|
import androidx.compose.ui.draw.clipToBounds
|
||||||
|
|
@ -53,6 +54,11 @@ import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalViewConfiguration
|
import androidx.compose.ui.platform.LocalViewConfiguration
|
||||||
import androidx.compose.ui.platform.ViewConfiguration
|
import androidx.compose.ui.platform.ViewConfiguration
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.invisibleToUser
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
|
import androidx.compose.ui.semantics.testTag
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -107,6 +113,7 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||||
import io.element.android.libraries.matrix.api.room.Mention
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
||||||
|
import io.element.android.libraries.testtags.TestTags
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
@ -256,6 +263,7 @@ private fun SwipeSensitivity(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun TimelineItemEventRowContent(
|
private fun TimelineItemEventRowContent(
|
||||||
event: TimelineItem.Event,
|
event: TimelineItem.Event,
|
||||||
|
|
@ -305,6 +313,11 @@ private fun TimelineItemEventRowContent(
|
||||||
.padding(horizontal = 16.dp)
|
.padding(horizontal = 16.dp)
|
||||||
.zIndex(1f)
|
.zIndex(1f)
|
||||||
.clickable(onClick = onUserDataClicked)
|
.clickable(onClick = onUserDataClicked)
|
||||||
|
// This is redundant when using talkback
|
||||||
|
.clearAndSetSemantics {
|
||||||
|
invisibleToUser()
|
||||||
|
testTag = TestTags.timelineItemSenderInfo.value
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,6 +426,7 @@ private fun MessageSenderInformation(
|
||||||
private fun MessageEventBubbleContent(
|
private fun MessageEventBubbleContent(
|
||||||
event: TimelineItem.Event,
|
event: TimelineItem.Event,
|
||||||
onMessageLongClick: () -> Unit,
|
onMessageLongClick: () -> Unit,
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
inReplyToClick: () -> Unit,
|
inReplyToClick: () -> Unit,
|
||||||
onTimestampClicked: () -> Unit,
|
onTimestampClicked: () -> Unit,
|
||||||
onMentionClicked: (Mention) -> Unit,
|
onMentionClicked: (Mention) -> Unit,
|
||||||
|
|
@ -445,6 +459,7 @@ private fun MessageEventBubbleContent(
|
||||||
text = stringResource(CommonStrings.common_thread),
|
text = stringResource(CommonStrings.common_thread),
|
||||||
style = ElementTheme.typography.fontBodyXsRegular,
|
style = ElementTheme.typography.fontBodyXsRegular,
|
||||||
color = ElementTheme.colors.textPrimary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
|
modifier = Modifier.clearAndSetSemantics { }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +595,8 @@ private fun MessageEventBubbleContent(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.padding(top = topPadding, start = 8.dp, end = 8.dp)
|
.padding(top = topPadding, start = 8.dp, end = 8.dp)
|
||||||
.clip(RoundedCornerShape(6.dp))
|
.clip(RoundedCornerShape(6.dp))
|
||||||
.clickable(enabled = true, onClick = inReplyToClick),
|
// FIXME when a node is clickable, its contents won't be added to the semantics tree of its parent
|
||||||
|
// .clickable(enabled = true, onClick = inReplyToClick)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (inReplyToDetails != null) {
|
if (inReplyToDetails != null) {
|
||||||
|
|
@ -611,7 +627,9 @@ private fun MessageEventBubbleContent(
|
||||||
timestampPosition = timestampPosition,
|
timestampPosition = timestampPosition,
|
||||||
inReplyToDetails = event.inReplyTo,
|
inReplyToDetails = event.inReplyTo,
|
||||||
canShrinkContent = event.content is TimelineItemVoiceContent,
|
canShrinkContent = event.content is TimelineItemVoiceContent,
|
||||||
modifier = bubbleModifier
|
modifier = bubbleModifier.semantics(mergeDescendants = true) {
|
||||||
|
contentDescription = event.safeSenderName
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,8 +659,12 @@ private fun ReplyToContent(
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
}
|
}
|
||||||
|
val a11InReplyToText = stringResource(CommonStrings.common_in_reply_to, senderName)
|
||||||
Column(verticalArrangement = Arrangement.SpaceBetween) {
|
Column(verticalArrangement = Arrangement.SpaceBetween) {
|
||||||
Text(
|
Text(
|
||||||
|
modifier = Modifier.semantics {
|
||||||
|
contentDescription = a11InReplyToText
|
||||||
|
},
|
||||||
text = senderName,
|
text = senderName,
|
||||||
style = ElementTheme.typography.fontBodySmMedium,
|
style = ElementTheme.typography.fontBodySmMedium,
|
||||||
textAlign = TextAlign.Start,
|
textAlign = TextAlign.Start,
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,9 @@ package io.element.android.features.messages.impl.timeline.components.event
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
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.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContentProvider
|
||||||
|
|
@ -25,15 +28,17 @@ import io.element.android.libraries.designsystem.components.BlurHashAsyncImage
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||||
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TimelineItemImageView(
|
fun TimelineItemImageView(
|
||||||
content: TimelineItemImageContent,
|
content: TimelineItemImageContent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val description = stringResource(CommonStrings.common_image)
|
||||||
TimelineItemAspectRatioBox(
|
TimelineItemAspectRatioBox(
|
||||||
aspectRatio = content.aspectRatio,
|
aspectRatio = content.aspectRatio,
|
||||||
modifier = modifier,
|
modifier = modifier.semantics { contentDescription = description },
|
||||||
) {
|
) {
|
||||||
BlurHashAsyncImage(
|
BlurHashAsyncImage(
|
||||||
model = MediaRequestData(content.preferredMediaSource, MediaRequestData.Kind.File(content.body, content.mimeType)),
|
model = MediaRequestData(content.preferredMediaSource, MediaRequestData.Kind.File(content.body, content.mimeType)),
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ import androidx.compose.material3.LocalTextStyle
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
import androidx.compose.runtime.CompositionLocalProvider
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.semantics.contentDescription
|
||||||
|
import androidx.compose.ui.semantics.semantics
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import io.element.android.compound.theme.ElementTheme
|
import io.element.android.compound.theme.ElementTheme
|
||||||
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout
|
import io.element.android.features.messages.impl.timeline.components.layout.ContentAvoidingLayout
|
||||||
|
|
@ -48,7 +50,7 @@ fun TimelineItemTextView(
|
||||||
val formattedBody = content.formattedBody
|
val formattedBody = content.formattedBody
|
||||||
val body = SpannableString(formattedBody ?: content.body)
|
val body = SpannableString(formattedBody ?: content.body)
|
||||||
|
|
||||||
Box(modifier) {
|
Box(modifier.semantics { contentDescription = body.toString() }) {
|
||||||
EditorStyledText(
|
EditorStyledText(
|
||||||
text = body,
|
text = body,
|
||||||
onLinkClickedListener = onLinkClicked,
|
onLinkClickedListener = onLinkClicked,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,8 @@ import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.res.stringResource
|
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.tooling.preview.PreviewParameter
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider
|
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContentProvider
|
||||||
|
|
@ -42,9 +44,10 @@ fun TimelineItemVideoView(
|
||||||
content: TimelineItemVideoContent,
|
content: TimelineItemVideoContent,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
val description = stringResource(CommonStrings.common_image)
|
||||||
TimelineItemAspectRatioBox(
|
TimelineItemAspectRatioBox(
|
||||||
aspectRatio = content.aspectRatio,
|
aspectRatio = content.aspectRatio,
|
||||||
modifier = modifier,
|
modifier = modifier.semantics { contentDescription = description },
|
||||||
contentAlignment = Alignment.Center,
|
contentAlignment = Alignment.Center,
|
||||||
) {
|
) {
|
||||||
BlurHashAsyncImage(
|
BlurHashAsyncImage(
|
||||||
|
|
|
||||||
|
|
@ -326,8 +326,7 @@ class MessagesViewTest {
|
||||||
state = state,
|
state = state,
|
||||||
onUserDataClicked = callback,
|
onUserDataClicked = callback,
|
||||||
)
|
)
|
||||||
val senderName = (timelineItem as? TimelineItem.Event)?.senderDisplayName.orEmpty()
|
rule.onNodeWithTag(TestTags.timelineItemSenderInfo.value).performClick()
|
||||||
rule.onNodeWithText(senderName).performClick()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -74,4 +74,9 @@ object TestTags {
|
||||||
val dialogPositive = TestTag("dialog-positive")
|
val dialogPositive = TestTag("dialog-positive")
|
||||||
val dialogNegative = TestTag("dialog-negative")
|
val dialogNegative = TestTag("dialog-negative")
|
||||||
val dialogNeutral = TestTag("dialog-neutral")
|
val dialogNeutral = TestTag("dialog-neutral")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Timeline item.
|
||||||
|
*/
|
||||||
|
val timelineItemSenderInfo = TestTag("timeline_item-sender_info")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue