Merge pull request #6650 from element-hq/feature/bma/a11yFixes

[a11y] Fix a set of issues
This commit is contained in:
Benoit Marty 2026-05-04 11:50:15 +02:00 committed by GitHub
commit abc540b379
44 changed files with 271 additions and 169 deletions

View file

@ -12,13 +12,12 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@ -28,7 +27,6 @@ import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.PreviewParameter
@ -91,33 +89,31 @@ fun MediaPlayerControllerView(
.widthIn(max = 480.dp),
verticalAlignment = Alignment.CenterVertically,
) {
val bgColor = if (state.isPlaying) {
ElementTheme.colors.bgCanvasDefault
val colors = if (state.isPlaying) {
IconButtonDefaults.iconButtonColors(
containerColor = ElementTheme.colors.bgCanvasDefault,
contentColor = ElementTheme.colors.iconPrimary,
)
} else {
ElementTheme.colors.textPrimary
IconButtonDefaults.iconButtonColors(
containerColor = ElementTheme.colors.iconPrimary,
contentColor = ElementTheme.colors.iconOnSolidPrimary,
)
}
Box(
IconButton(
modifier = Modifier
.size(36.dp)
.background(
color = bgColor,
shape = CircleShape,
)
.clip(CircleShape)
.clickable { onTogglePlay() }
.padding(8.dp),
contentAlignment = Alignment.Center,
.size(36.dp),
onClick = onTogglePlay,
colors = colors,
) {
if (state.isPlaying) {
Icon(
imageVector = CompoundIcons.PauseSolid(),
tint = ElementTheme.colors.iconPrimary,
contentDescription = stringResource(CommonStrings.a11y_pause)
)
} else {
Icon(
imageVector = CompoundIcons.PlaySolid(),
tint = ElementTheme.colors.iconOnSolidPrimary,
contentDescription = stringResource(CommonStrings.a11y_play)
)
}

View file

@ -120,6 +120,52 @@ fun MediaViewerView(
Scaffold(
modifier,
containerColor = Color.Transparent,
topBar = {
AnimatedVisibility(
visible = showOverlay,
enter = fadeIn(),
exit = fadeOut(),
) {
when (currentData) {
is MediaViewerPageData.MediaViewerData -> {
MediaViewerTopBar(
data = currentData,
canShowInfo = state.canShowInfo,
onBackClick = onBackClick,
onShareClick = {
state.eventSink(MediaViewerEvent.Share(currentData))
},
onSaveClick = {
state.eventSink(MediaViewerEvent.SaveOnDisk(currentData))
},
onInfoClick = {
state.eventSink(MediaViewerEvent.OpenInfo(currentData))
},
)
}
else -> {
TopAppBar(
title = {
if (currentData is MediaViewerPageData.Loading) {
Text(
modifier = Modifier.semantics {
heading()
},
text = stringResource(id = CommonStrings.common_loading_more),
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textPrimary,
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = bgCanvasWithTransparency,
),
navigationIcon = { BackButton(onClick = onBackClick) },
)
}
}
}
},
snackbarHost = { SnackbarHost(snackbarHostState) },
) {
val pagerState = rememberPagerState(state.currentIndex, 0f) {
@ -186,69 +232,22 @@ fun MediaViewerView(
isUserSelected = (state.listData[page] as? MediaViewerPageData.MediaViewerData)?.eventId == state.initiallySelectedEventId,
)
// Bottom bar
AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) {
Box(
modifier = Modifier.fillMaxSize()
) {
MediaViewerBottomBar(
modifier = Modifier.align(Alignment.BottomCenter),
showDivider = dataForPage.mediaInfo.mimeType.isMimeTypeVideo(),
caption = dataForPage.mediaInfo.caption,
onHeightChange = { bottomPaddingInPixels = it },
)
}
AnimatedVisibility(
visible = showOverlay,
enter = fadeIn(),
exit = fadeOut(),
modifier = Modifier.align(Alignment.BottomCenter),
) {
MediaViewerBottomBar(
showDivider = dataForPage.mediaInfo.mimeType.isMimeTypeVideo(),
caption = dataForPage.mediaInfo.caption,
onHeightChange = { bottomPaddingInPixels = it },
)
}
}
}
}
}
// Top bar
AnimatedVisibility(visible = showOverlay, enter = fadeIn(), exit = fadeOut()) {
Box(
modifier = Modifier
.fillMaxSize()
.navigationBarsPadding()
) {
when (currentData) {
is MediaViewerPageData.MediaViewerData -> {
MediaViewerTopBar(
data = currentData,
canShowInfo = state.canShowInfo,
onBackClick = onBackClick,
onShareClick = {
state.eventSink(MediaViewerEvent.Share(currentData))
},
onSaveClick = {
state.eventSink(MediaViewerEvent.SaveOnDisk(currentData))
},
onInfoClick = {
state.eventSink(MediaViewerEvent.OpenInfo(currentData))
},
)
}
else -> {
TopAppBar(
title = {
if (currentData is MediaViewerPageData.Loading) {
Text(
modifier = Modifier.semantics {
heading()
},
text = stringResource(id = CommonStrings.common_loading_more),
style = ElementTheme.typography.fontBodyMdMedium,
color = ElementTheme.colors.textPrimary,
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = bgCanvasWithTransparency,
),
navigationIcon = { BackButton(onClick = onBackClick) },
)
}
}
}
}
}
when (val bottomSheetState = state.mediaBottomSheetState) {
@ -373,11 +372,12 @@ private fun MediaViewerPage(
isUserSelected = isUserSelected,
audioFocus = audioFocus,
)
ThumbnailView(
mediaInfo = data.mediaInfo,
thumbnailSource = data.thumbnailSource,
isVisible = showThumbnail,
)
if (showThumbnail) {
ThumbnailView(
mediaInfo = data.mediaInfo,
thumbnailSource = data.thumbnailSource,
)
}
if (showError) {
ErrorView(
errorMessage = stringResource(id = CommonStrings.error_unknown),
@ -603,7 +603,6 @@ private val maxCaptionHeightLandscape = 128.dp
@Composable
private fun ThumbnailView(
thumbnailSource: MediaSource?,
isVisible: Boolean,
mediaInfo: MediaInfo,
modifier: Modifier = Modifier,
) {
@ -611,21 +610,19 @@ private fun ThumbnailView(
modifier = modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
if (isVisible) {
val mediaRequestData = MediaRequestData(
source = thumbnailSource,
kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType)
)
val alpha = if (LocalInspectionMode.current) 0.1f else 1f
AsyncImage(
modifier = Modifier
.fillMaxSize()
.alpha(alpha),
model = mediaRequestData,
contentScale = ContentScale.Fit,
contentDescription = null,
)
}
val mediaRequestData = MediaRequestData(
source = thumbnailSource,
kind = MediaRequestData.Kind.File(mediaInfo.filename, mediaInfo.mimeType)
)
val alpha = if (LocalInspectionMode.current) 0.1f else 1f
AsyncImage(
modifier = Modifier
.fillMaxSize()
.alpha(alpha),
model = mediaRequestData,
contentScale = ContentScale.Fit,
contentDescription = null,
)
}
}