Merge pull request #6650 from element-hq/feature/bma/a11yFixes
[a11y] Fix a set of issues
This commit is contained in:
commit
abc540b379
44 changed files with 271 additions and 169 deletions
|
|
@ -19,6 +19,9 @@ import androidx.compose.material3.ExperimentalMaterial3Api
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.element.android.compound.theme.ElementTheme
|
||||
|
|
@ -90,7 +93,11 @@ fun ChooseSelfVerificationModeView(
|
|||
Text(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onLearnMore)
|
||||
.padding(vertical = 4.dp, horizontal = 16.dp),
|
||||
.padding(vertical = 4.dp, horizontal = 16.dp)
|
||||
.semantics {
|
||||
// Note: there is no Role.Link, so we use Role.Button for better accessibility support
|
||||
role = Role.Button
|
||||
},
|
||||
text = stringResource(CommonStrings.action_learn_more),
|
||||
style = ElementTheme.typography.fontBodyLgMedium
|
||||
)
|
||||
|
|
|
|||
|
|
@ -237,6 +237,7 @@ private fun SpaceFilterButton(
|
|||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
val isSelected = spaceFiltersState is SpaceFiltersState.Selected
|
||||
IconButton(
|
||||
onClick = ::onClick,
|
||||
|
|
@ -320,7 +321,15 @@ private fun AccountIcon(
|
|||
Avatar(
|
||||
avatarData = avatarData,
|
||||
avatarType = AvatarType.User,
|
||||
contentDescription = if (isCurrentAccount) stringResource(CommonStrings.common_settings) else null,
|
||||
contentDescription = if (isCurrentAccount) {
|
||||
if (showAvatarIndicator) {
|
||||
stringResource(CommonStrings.a11y_settings_with_required_action)
|
||||
} else {
|
||||
stringResource(CommonStrings.common_settings)
|
||||
}
|
||||
} else {
|
||||
null
|
||||
},
|
||||
)
|
||||
if (showAvatarIndicator) {
|
||||
RedIndicatorAtom(
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ class MessagesFlowNode(
|
|||
val mediaInfo: MediaInfo,
|
||||
val mediaSource: MediaSource,
|
||||
val thumbnailSource: MediaSource?,
|
||||
val canUseOverlay: Boolean,
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
|
|
@ -227,10 +228,11 @@ class MessagesFlowNode(
|
|||
callback.navigateToRoomDetails()
|
||||
}
|
||||
|
||||
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
||||
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean {
|
||||
return processEventClick(
|
||||
timelineMode = timelineMode,
|
||||
event = event,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -320,7 +322,11 @@ class MessagesFlowNode(
|
|||
)
|
||||
val callback = object : MediaViewerEntryPoint.Callback {
|
||||
override fun onDone() {
|
||||
overlay.hide()
|
||||
if (navTarget.canUseOverlay) {
|
||||
overlay.hide()
|
||||
} else {
|
||||
backstack.pop()
|
||||
}
|
||||
}
|
||||
|
||||
override fun viewInTimeline(eventId: EventId) {
|
||||
|
|
@ -414,10 +420,11 @@ class MessagesFlowNode(
|
|||
}
|
||||
NavTarget.PinnedMessagesList -> {
|
||||
val callback = object : PinnedMessagesListNode.Callback {
|
||||
override fun handleEventClick(event: TimelineItem.Event) {
|
||||
override fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean) {
|
||||
processEventClick(
|
||||
timelineMode = Timeline.Mode.PinnedEvents,
|
||||
event = event,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -456,10 +463,11 @@ class MessagesFlowNode(
|
|||
focusedEventId = navTarget.focusedEventId,
|
||||
)
|
||||
val callback = object : ThreadedMessagesNode.Callback {
|
||||
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean {
|
||||
override fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean {
|
||||
return processEventClick(
|
||||
timelineMode = timelineMode,
|
||||
event = event,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -547,6 +555,7 @@ class MessagesFlowNode(
|
|||
private fun processEventClick(
|
||||
timelineMode: Timeline.Mode,
|
||||
event: TimelineItem.Event,
|
||||
canUseOverlay: Boolean,
|
||||
): Boolean {
|
||||
val navTarget = when (event.content) {
|
||||
is TimelineItemImageContent -> {
|
||||
|
|
@ -556,6 +565,7 @@ class MessagesFlowNode(
|
|||
content = event.content,
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
is TimelineItemVideoContent -> {
|
||||
|
|
@ -565,6 +575,7 @@ class MessagesFlowNode(
|
|||
content = event.content,
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
is TimelineItemFileContent -> {
|
||||
|
|
@ -574,6 +585,7 @@ class MessagesFlowNode(
|
|||
content = event.content,
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = event.content.thumbnailSource,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
is TimelineItemAudioContent -> {
|
||||
|
|
@ -583,6 +595,7 @@ class MessagesFlowNode(
|
|||
content = event.content,
|
||||
mediaSource = event.content.mediaSource,
|
||||
thumbnailSource = null,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
is TimelineItemLocationContent -> {
|
||||
|
|
@ -603,7 +616,11 @@ class MessagesFlowNode(
|
|||
}
|
||||
return when (navTarget) {
|
||||
is NavTarget.MediaViewer -> {
|
||||
overlay.show(navTarget)
|
||||
if (canUseOverlay) {
|
||||
overlay.show(navTarget)
|
||||
} else {
|
||||
backstack.push(navTarget)
|
||||
}
|
||||
true
|
||||
}
|
||||
is NavTarget.LocationViewer -> {
|
||||
|
|
@ -620,6 +637,7 @@ class MessagesFlowNode(
|
|||
content: TimelineItemEventContentWithAttachment,
|
||||
mediaSource: MediaSource,
|
||||
thumbnailSource: MediaSource?,
|
||||
canUseOverlay: Boolean,
|
||||
): NavTarget {
|
||||
return NavTarget.MediaViewer(
|
||||
mode = mode,
|
||||
|
|
@ -647,6 +665,7 @@ class MessagesFlowNode(
|
|||
),
|
||||
mediaSource = mediaSource,
|
||||
thumbnailSource = thumbnailSource,
|
||||
canUseOverlay = canUseOverlay,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ import io.element.android.libraries.matrix.api.timeline.Timeline
|
|||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.services.analytics.api.AnalyticsLongRunningTransaction.LoadMessagesUi
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.analytics.api.finishLongRunningTransaction
|
||||
|
|
@ -115,7 +117,7 @@ class MessagesNode(
|
|||
)
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
|
||||
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean
|
||||
fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?)
|
||||
fun navigateToRoomMemberDetails(userId: UserId)
|
||||
fun handlePermalinkClick(data: PermalinkData)
|
||||
|
|
@ -247,6 +249,7 @@ class MessagesNode(
|
|||
override fun View(modifier: Modifier) {
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard()
|
||||
CompositionLocalProvider(
|
||||
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||
) {
|
||||
|
|
@ -268,11 +271,11 @@ class MessagesNode(
|
|||
onRoomDetailsClick = callback::navigateToRoomDetails,
|
||||
onEventContentClick = { isLive, event ->
|
||||
if (isLive) {
|
||||
callback.handleEventClick(timelineController.mainTimelineMode(), event)
|
||||
callback.handleEventClick(timelineController.mainTimelineMode(), event, canUseOverlay)
|
||||
} else {
|
||||
val detachedTimelineMode = timelineController.detachedTimelineMode()
|
||||
if (detachedTimelineMode != null) {
|
||||
callback.handleEventClick(detachedTimelineMode, event)
|
||||
callback.handleEventClick(detachedTimelineMode, event, canUseOverlay)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@ import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
|||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
|
||||
@ContributesNode(RoomScope::class)
|
||||
@AssistedInject
|
||||
|
|
@ -50,7 +52,7 @@ class PinnedMessagesListNode(
|
|||
private val permalinkParser: PermalinkParser,
|
||||
) : Node(buildContext, plugins = plugins), PinnedMessagesListNavigator {
|
||||
interface Callback : Plugin {
|
||||
fun handleEventClick(event: TimelineItem.Event)
|
||||
fun handleEventClick(event: TimelineItem.Event, canUseOverlay: Boolean)
|
||||
fun navigateToRoomMemberDetails(userId: UserId)
|
||||
fun viewInTimeline(eventId: EventId)
|
||||
fun handlePermalinkClick(data: PermalinkData.RoomLink)
|
||||
|
|
@ -103,6 +105,7 @@ class PinnedMessagesListNode(
|
|||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard()
|
||||
CompositionLocalProvider(
|
||||
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||
) {
|
||||
|
|
@ -113,7 +116,9 @@ class PinnedMessagesListNode(
|
|||
PinnedMessagesListView(
|
||||
state = state,
|
||||
onBackClick = ::navigateUp,
|
||||
onEventClick = callback::handleEventClick,
|
||||
onEventClick = {
|
||||
callback.handleEventClick(it, canUseOverlay)
|
||||
},
|
||||
onUserDataClick = { callback.navigateToRoomMemberDetails(it.userId) },
|
||||
onLinkClick = { link -> onLinkClick(context, link.url) },
|
||||
onLinkLongClick = {
|
||||
|
|
|
|||
|
|
@ -68,6 +68,8 @@ import io.element.android.libraries.matrix.api.room.alias.matches
|
|||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
||||
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -124,7 +126,7 @@ class ThreadedMessagesNode(
|
|||
}
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event): Boolean
|
||||
fun handleEventClick(timelineMode: Timeline.Mode, event: TimelineItem.Event, canUseOverlay: Boolean): Boolean
|
||||
fun navigateToPreviewAttachments(attachments: ImmutableList<Attachment>, inReplyToEventId: EventId?)
|
||||
fun navigateToRoomMemberDetails(userId: UserId)
|
||||
fun handlePermalinkClick(data: PermalinkData)
|
||||
|
|
@ -252,6 +254,7 @@ class ThreadedMessagesNode(
|
|||
override fun View(modifier: Modifier) {
|
||||
val activity = requireNotNull(LocalActivity.current)
|
||||
val isDark = ElementTheme.isLightTheme.not()
|
||||
val canUseOverlay = !isTalkbackActive() && !hasExternalKeyboard()
|
||||
CompositionLocalProvider(
|
||||
LocalTimelineItemPresenterFactories provides timelineItemPresenterFactories,
|
||||
) {
|
||||
|
|
@ -271,11 +274,11 @@ class ThreadedMessagesNode(
|
|||
onEventContentClick = { isLive, event ->
|
||||
timelineController?.let { controller ->
|
||||
if (isLive) {
|
||||
callback.handleEventClick(controller.mainTimelineMode(), event)
|
||||
callback.handleEventClick(controller.mainTimelineMode(), event, canUseOverlay)
|
||||
} else {
|
||||
val detachedTimelineMode = controller.detachedTimelineMode()
|
||||
if (detachedTimelineMode != null) {
|
||||
callback.handleEventClick(detachedTimelineMode, event)
|
||||
callback.handleEventClick(detachedTimelineMode, event, canUseOverlay)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser
|
|||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ import io.element.android.libraries.designsystem.theme.messageFromMeBackground
|
|||
import io.element.android.libraries.designsystem.theme.messageFromOtherBackground
|
||||
import io.element.android.libraries.testtags.TestTags
|
||||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.graphics.drawInLayer
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
|
||||
private val BUBBLE_RADIUS = 12.dp
|
||||
private val avatarRadius = AvatarSize.TimelineSender.dp / 2
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ import io.element.android.libraries.testtags.TestTags
|
|||
import io.element.android.libraries.testtags.testTag
|
||||
import io.element.android.libraries.ui.strings.CommonPlurals
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.abs
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|||
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.user.MatrixUser
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ 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.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
import kotlin.time.DurationUnit
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ import io.element.android.libraries.designsystem.preview.ElementPreview
|
|||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.compose.EditorStyledText
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import io.element.android.libraries.matrix.ui.media.MAX_THUMBNAIL_WIDTH
|
|||
import io.element.android.libraries.matrix.ui.media.MediaRequestData
|
||||
import io.element.android.libraries.textcomposer.ElementRichTextEditorStyle
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.wysiwyg.compose.EditorStyledText
|
||||
import io.element.android.wysiwyg.link.Link
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ import io.element.android.libraries.designsystem.theme.components.Icon
|
|||
import io.element.android.libraries.designsystem.theme.components.IconButton
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.time.isTalkbackActive
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.libraries.voiceplayer.api.VoiceMessageEvent
|
||||
import io.element.android.libraries.voiceplayer.api.VoiceMessageState
|
||||
import io.element.android.libraries.voiceplayer.api.VoiceMessageStateProvider
|
||||
|
|
|
|||
|
|
@ -24,8 +24,10 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.semantics.contentDescription
|
||||
import androidx.compose.ui.semantics.focused
|
||||
import androidx.compose.ui.semantics.role
|
||||
import androidx.compose.ui.semantics.semantics
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -227,7 +229,11 @@ private fun ContentInitial(
|
|||
Text(
|
||||
modifier = Modifier
|
||||
.clickable { onLearnMoreClick() }
|
||||
.padding(vertical = 4.dp, horizontal = 16.dp),
|
||||
.padding(vertical = 4.dp, horizontal = 16.dp)
|
||||
.semantics {
|
||||
// Note: there is no Role.Link, so we use Role.Button for better accessibility support
|
||||
role = Role.Button
|
||||
},
|
||||
text = stringResource(CommonStrings.action_learn_more),
|
||||
style = ElementTheme.typography.fontBodyLgMedium
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue