From a4c6febfbce1102d4b8e3d856fb428f4168acfe1 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 7 Nov 2025 16:42:33 -0300 Subject: [PATCH] Improve screenreader support --- next/src/main/kotlin/org/fdroid/ui/About.kt | 3 ++ .../kotlin/org/fdroid/ui/MainOverflowMenu.kt | 9 +++- .../org/fdroid/ui/apps/InstalledAppRow.kt | 6 ++- .../org/fdroid/ui/apps/UpdatableAppRow.kt | 27 +++++++----- .../org/fdroid/ui/categories/CategoryChip.kt | 3 ++ .../org/fdroid/ui/crash/CrashContent.kt | 3 ++ .../org/fdroid/ui/details/AppDetails.kt | 9 +++- .../kotlin/org/fdroid/ui/details/Versions.kt | 22 +++++----- .../org/fdroid/ui/discover/AppCarousel.kt | 6 ++- .../org/fdroid/ui/discover/AppsSearch.kt | 8 +++- .../kotlin/org/fdroid/ui/discover/Discover.kt | 13 ++++-- .../kotlin/org/fdroid/ui/lists/AppList.kt | 4 +- .../kotlin/org/fdroid/ui/lists/AppListRow.kt | 6 ++- .../kotlin/org/fdroid/ui/lists/AppsFilter.kt | 7 +++- .../ui/repositories/add/AddRepoErrorScreen.kt | 2 +- .../ui/repositories/add/AddRepoIntro.kt | 28 +++++++------ .../ui/repositories/details/BasicAuth.kt | 10 ++++- .../ui/repositories/details/RepoDetails.kt | 16 ++++++-- .../org/fdroid/ui/settings/PreferenceProxy.kt | 3 ++ .../kotlin/org/fdroid/ui/settings/Settings.kt | 8 ++++ .../kotlin/org/fdroid/ui/utils/ExpandIcon.kt | 41 +++++++++++++++++++ .../org/fdroid/ui/utils/ExpandableSection.kt | 22 +++++----- next/src/main/res/values/strings-next.xml | 1 + 23 files changed, 192 insertions(+), 65 deletions(-) create mode 100644 next/src/main/kotlin/org/fdroid/ui/utils/ExpandIcon.kt diff --git a/next/src/main/kotlin/org/fdroid/ui/About.kt b/next/src/main/kotlin/org/fdroid/ui/About.kt index 52cb4b767..82ee49c79 100644 --- a/next/src/main/kotlin/org/fdroid/ui/About.kt +++ b/next/src/main/kotlin/org/fdroid/ui/About.kt @@ -29,6 +29,8 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -109,6 +111,7 @@ fun AboutHeader(modifier: Modifier = Modifier) { ) { Image( painter = painterResource(id = R.drawable.ic_launcher), + modifier = Modifier.semantics { hideFromAccessibility() }, contentDescription = null, // decorative element ) SelectionContainer { diff --git a/next/src/main/kotlin/org/fdroid/ui/MainOverflowMenu.kt b/next/src/main/kotlin/org/fdroid/ui/MainOverflowMenu.kt index 5d561ce6a..71cda2d27 100644 --- a/next/src/main/kotlin/org/fdroid/ui/MainOverflowMenu.kt +++ b/next/src/main/kotlin/org/fdroid/ui/MainOverflowMenu.kt @@ -5,7 +5,10 @@ import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics @Composable fun MainOverFlowMenu( @@ -22,7 +25,11 @@ fun MainOverFlowMenu( text = { Text(stringResource(dest.label)) }, onClick = { onItemClicked(dest) }, leadingIcon = { - Icon(dest.icon, contentDescription = null) + Icon( + imageVector = dest.icon, + contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, + ) } ) } diff --git a/next/src/main/kotlin/org/fdroid/ui/apps/InstalledAppRow.kt b/next/src/main/kotlin/org/fdroid/ui/apps/InstalledAppRow.kt index 29bc27041..d0676bab7 100644 --- a/next/src/main/kotlin/org/fdroid/ui/apps/InstalledAppRow.kt +++ b/next/src/main/kotlin/org/fdroid/ui/apps/InstalledAppRow.kt @@ -10,6 +10,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -32,7 +34,9 @@ fun InstalledAppRow( model = PackageName(app.packageName, app.iconModel as? DownloadRequest), error = painterResource(R.drawable.ic_repo_app_default), contentDescription = null, - modifier = Modifier.size(48.dp), + modifier = Modifier + .size(48.dp) + .semantics { hideFromAccessibility() }, ) }, headlineContent = { diff --git a/next/src/main/kotlin/org/fdroid/ui/apps/UpdatableAppRow.kt b/next/src/main/kotlin/org/fdroid/ui/apps/UpdatableAppRow.kt index e4302fdbb..fed0f7735 100644 --- a/next/src/main/kotlin/org/fdroid/ui/apps/UpdatableAppRow.kt +++ b/next/src/main/kotlin/org/fdroid/ui/apps/UpdatableAppRow.kt @@ -7,8 +7,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.NewReleases import androidx.compose.material3.BadgedBox import androidx.compose.material3.Card @@ -27,6 +25,10 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -34,6 +36,7 @@ import org.fdroid.download.DownloadRequest import org.fdroid.download.PackageName import org.fdroid.fdroid.ui.theme.FDroidContent import org.fdroid.ui.utils.AsyncShimmerImage +import org.fdroid.ui.utils.ExpandIconArrow import org.fdroid.ui.utils.getPreviewVersion @Composable @@ -50,7 +53,9 @@ fun UpdatableAppRow( Icon( imageVector = Icons.Filled.NewReleases, tint = MaterialTheme.colorScheme.error, - contentDescription = null, modifier = Modifier.size(24.dp), + contentDescription = + stringResource(R.string.notification_title_single_update_available), + modifier = Modifier.size(24.dp), ) }) { AsyncShimmerImage( @@ -72,11 +77,7 @@ fun UpdatableAppRow( }, trailingContent = { if (app.whatsNew != null) IconButton(onClick = { isExpanded = !isExpanded }) { - if (isExpanded) { - Icon(Icons.Default.ArrowDropUp, "TODO") - } else { - Icon(Icons.Default.ArrowDropDown, "TODO") - } + ExpandIconArrow(isExpanded) } }, colors = ListItemDefaults.colors( @@ -88,12 +89,18 @@ fun UpdatableAppRow( ), modifier = modifier, ) - AnimatedVisibility(visible = isExpanded, modifier = Modifier.padding(8.dp)) { + AnimatedVisibility( + visible = isExpanded, + modifier = Modifier + .padding(8.dp) + .semantics { liveRegion = LiveRegionMode.Polite } + ) { Card(modifier = Modifier.fillMaxWidth()) { Text( text = app.whatsNew ?: "", style = MaterialTheme.typography.bodyMedium, - modifier = Modifier.padding(8.dp) + modifier = Modifier + .padding(8.dp) ) } } diff --git a/next/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt b/next/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt index 24a20eb19..4e116db38 100644 --- a/next/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt +++ b/next/src/main/kotlin/org/fdroid/ui/categories/CategoryChip.kt @@ -11,6 +11,8 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -34,6 +36,7 @@ fun CategoryChip( imageVector = categoryItem.imageVector, contentDescription = null, tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, label = { diff --git a/next/src/main/kotlin/org/fdroid/ui/crash/CrashContent.kt b/next/src/main/kotlin/org/fdroid/ui/crash/CrashContent.kt index 79c3bdecf..6dc52d809 100644 --- a/next/src/main/kotlin/org/fdroid/ui/crash/CrashContent.kt +++ b/next/src/main/kotlin/org/fdroid/ui/crash/CrashContent.kt @@ -24,6 +24,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -54,6 +56,7 @@ fun CrashContent( .fillMaxWidth(0.5f) .aspectRatio(1f) .padding(vertical = 16.dp) + .semantics { hideFromAccessibility() }, ) Column(verticalArrangement = spacedBy(16.dp)) { Text( diff --git a/next/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt b/next/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt index bf4cf3fbb..e8d3f87f1 100644 --- a/next/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt +++ b/next/src/main/kotlin/org/fdroid/ui/details/AppDetails.kt @@ -45,6 +45,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.LinkAnnotation import androidx.compose.ui.text.buildAnnotatedString @@ -146,7 +149,11 @@ fun AppDetails( var descriptionExpanded by remember { mutableStateOf(false) } item.description?.let { description -> val htmlDescription = AnnotatedString.fromHtml(description) - AnimatedVisibility(descriptionExpanded) { + AnimatedVisibility( + visible = descriptionExpanded, + modifier = Modifier + .semantics { liveRegion = LiveRegionMode.Polite }, + ) { SelectionContainer { Text( text = htmlDescription, diff --git a/next/src/main/kotlin/org/fdroid/ui/details/Versions.kt b/next/src/main/kotlin/org/fdroid/ui/details/Versions.kt index 5f1a786e3..d0221be7b 100644 --- a/next/src/main/kotlin/org/fdroid/ui/details/Versions.kt +++ b/next/src/main/kotlin/org/fdroid/ui/details/Versions.kt @@ -9,11 +9,8 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.AccessTime -import androidx.compose.material.icons.filled.ExpandLess -import androidx.compose.material.icons.filled.ExpandMore import androidx.compose.material3.Badge import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -28,6 +25,9 @@ import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -35,6 +35,7 @@ import kotlinx.coroutines.launch import org.fdroid.R import org.fdroid.database.AppVersion import org.fdroid.fdroid.ui.theme.FDroidContent +import org.fdroid.ui.utils.ExpandIconChevron import org.fdroid.ui.utils.ExpandableSection import org.fdroid.ui.utils.FDroidOutlineButton import org.fdroid.ui.utils.asRelativeTimeString @@ -83,14 +84,7 @@ fun Version( expanded = !expanded } ) { - Icon( - imageVector = if (expanded) { - Icons.Default.ExpandLess - } else { - Icons.Default.ExpandMore - }, - contentDescription = null, - ) + ExpandIconChevron(expanded) Row { Column(modifier = Modifier.weight(1f)) { Text( @@ -129,7 +123,11 @@ fun Version( } } } - AnimatedVisibility(expanded) { + AnimatedVisibility( + visible = expanded, + modifier = Modifier + .semantics { liveRegion = LiveRegionMode.Polite } + ) { Row( horizontalArrangement = spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, diff --git a/next/src/main/kotlin/org/fdroid/ui/discover/AppCarousel.kt b/next/src/main/kotlin/org/fdroid/ui/discover/AppCarousel.kt index 8086f1a0e..f12f41d4c 100644 --- a/next/src/main/kotlin/org/fdroid/ui/discover/AppCarousel.kt +++ b/next/src/main/kotlin/org/fdroid/ui/discover/AppCarousel.kt @@ -21,6 +21,8 @@ import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.fdroid.ui.theme.FDroidContent @@ -51,6 +53,7 @@ fun AppCarousel( ) Icon( imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, + modifier = Modifier.semantics { hideFromAccessibility() }, contentDescription = null, ) } @@ -79,7 +82,8 @@ fun AppBox(app: AppDiscoverItem, onAppTap: (AppDiscoverItem) -> Unit) { contentScale = ContentScale.Fit, modifier = Modifier .requiredSize(76.dp) - .clip(MaterialTheme.shapes.medium), + .clip(MaterialTheme.shapes.medium) + .semantics { hideFromAccessibility() }, ) Text( text = app.name, diff --git a/next/src/main/kotlin/org/fdroid/ui/discover/AppsSearch.kt b/next/src/main/kotlin/org/fdroid/ui/discover/AppsSearch.kt index 81927dad0..e04ef2b20 100644 --- a/next/src/main/kotlin/org/fdroid/ui/discover/AppsSearch.kt +++ b/next/src/main/kotlin/org/fdroid/ui/discover/AppsSearch.kt @@ -27,6 +27,8 @@ import androidx.compose.material3.rememberSearchBarState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -65,7 +67,11 @@ fun AppsSearch( textFieldState = textFieldState, placeholder = { Text(stringResource(R.string.search_placeholder)) }, leadingIcon = { - Icon(imageVector = Icons.Default.Search, contentDescription = null) + Icon( + imageVector = Icons.Default.Search, + contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, + ) }, onSearch = { }, ) diff --git a/next/src/main/kotlin/org/fdroid/ui/discover/Discover.kt b/next/src/main/kotlin/org/fdroid/ui/discover/Discover.kt index f071cb5fd..b16493b54 100644 --- a/next/src/main/kotlin/org/fdroid/ui/discover/Discover.kt +++ b/next/src/main/kotlin/org/fdroid/ui/discover/Discover.kt @@ -31,6 +31,9 @@ import androidx.compose.ui.Alignment.Companion.End import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -79,8 +82,8 @@ fun Discover( var menuExpanded by remember { mutableStateOf(false) } IconButton(onClick = { menuExpanded = !menuExpanded }) { Icon( - Icons.Default.MoreVert, - contentDescription = null, + imageVector = Icons.Default.MoreVert, + contentDescription = stringResource(R.string.more), ) } MainOverFlowMenu(menuExpanded, { @@ -107,7 +110,11 @@ fun Discover( ) { when (discoverModel) { is LoadingDiscoverModel -> { - AnimatedVisibility(discoverModel.isFirstStart) { + AnimatedVisibility( + visible = discoverModel.isFirstStart, + modifier = Modifier + .semantics { liveRegion = LiveRegionMode.Polite }, + ) { Text( stringResource(R.string.first_start_loading), textAlign = TextAlign.Center, diff --git a/next/src/main/kotlin/org/fdroid/ui/lists/AppList.kt b/next/src/main/kotlin/org/fdroid/ui/lists/AppList.kt index e10e542ab..91d7ddcbf 100644 --- a/next/src/main/kotlin/org/fdroid/ui/lists/AppList.kt +++ b/next/src/main/kotlin/org/fdroid/ui/lists/AppList.kt @@ -144,9 +144,7 @@ fun AppList( }) { Icon( imageVector = Icons.Filled.FilterList, - contentDescription = stringResource( - R.string.search_filter_no_results - ), + contentDescription = stringResource(R.string.filter), ) } } diff --git a/next/src/main/kotlin/org/fdroid/ui/lists/AppListRow.kt b/next/src/main/kotlin/org/fdroid/ui/lists/AppListRow.kt index 092b3849d..5d74acc24 100644 --- a/next/src/main/kotlin/org/fdroid/ui/lists/AppListRow.kt +++ b/next/src/main/kotlin/org/fdroid/ui/lists/AppListRow.kt @@ -11,6 +11,8 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -31,7 +33,9 @@ fun AppListRow( model = item.iconModel, error = painterResource(R.drawable.ic_repo_app_default), contentDescription = null, - modifier = Modifier.size(48.dp), + modifier = Modifier + .size(48.dp) + .semantics { hideFromAccessibility() }, ) }, colors = ListItemDefaults.colors( diff --git a/next/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt b/next/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt index 19a402f1e..cdfecff49 100644 --- a/next/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt +++ b/next/src/main/kotlin/org/fdroid/ui/lists/AppsFilter.kt @@ -32,6 +32,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -181,7 +183,9 @@ fun AppsFilter( } else AsyncShimmerImage( model = repo.icon, contentDescription = null, - modifier = Modifier.size(24.dp) + modifier = Modifier + .size(24.dp) + .semantics { hideFromAccessibility() }, ) }, label = { @@ -223,6 +227,7 @@ private fun FilterHeader(icon: ImageVector, text: String) { imageVector = icon, contentDescription = null, tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.semantics { hideFromAccessibility() }, ) Text(text = text, style = MaterialTheme.typography.titleMedium) } diff --git a/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoErrorScreen.kt b/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoErrorScreen.kt index 562eb5d6e..679b6c1d9 100644 --- a/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoErrorScreen.kt +++ b/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoErrorScreen.kt @@ -46,7 +46,7 @@ fun AddRepoErrorScreen(state: AddRepoError, modifier: Modifier = Modifier) { ) { Image( imageVector = Icons.Default.Error, - contentDescription = null, + contentDescription = stringResource(R.string.error), colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.error), modifier = Modifier.size(48.dp), ) diff --git a/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoIntro.kt b/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoIntro.kt index de22b8408..14e3d2277 100644 --- a/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoIntro.kt +++ b/next/src/main/kotlin/org/fdroid/ui/repositories/add/AddRepoIntro.kt @@ -25,14 +25,11 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDropDown -import androidx.compose.material.icons.filled.ArrowDropUp import androidx.compose.material.icons.filled.ContentPaste import androidx.compose.material.icons.filled.QrCode import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextField @@ -54,6 +51,9 @@ import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.style.TextAlign @@ -69,6 +69,7 @@ import kotlinx.coroutines.launch import org.fdroid.R import org.fdroid.fdroid.ui.theme.FDroidContent import org.fdroid.repo.None +import org.fdroid.ui.utils.ExpandIconArrow import org.fdroid.ui.utils.FDroidButton import org.fdroid.ui.utils.FDroidOutlineButton @@ -124,7 +125,11 @@ fun AddRepoIntroContent(onFetchRepo: (String) -> Unit, modifier: Modifier = Modi } }, ) - AnimatedVisibility(showPermissionWarning) { + AnimatedVisibility( + visible = showPermissionWarning, + modifier = Modifier + .semantics { liveRegion = LiveRegionMode.Polite }, + ) { Card( colors = CardDefaults.cardColors( containerColor = MaterialTheme.colorScheme.inverseSurface, @@ -161,19 +166,16 @@ fun AddRepoIntroContent(onFetchRepo: (String) -> Unit, modifier: Modifier = Modi // avoid occupying the whole row modifier = Modifier.weight(1f), ) - Icon( - imageVector = if (manualExpanded) { - Icons.Default.ArrowDropUp - } else { - Icons.Default.ArrowDropDown - }, - contentDescription = null, - ) + ExpandIconArrow(manualExpanded) } val textState = remember { mutableStateOf(TextFieldValue()) } val focusRequester = remember { FocusRequester() } val coroutineScope = rememberCoroutineScope() - AnimatedVisibility(visible = manualExpanded) { + AnimatedVisibility( + visible = manualExpanded, + modifier = Modifier + .semantics { liveRegion = LiveRegionMode.Polite }, + ) { Column( horizontalAlignment = Alignment.End, verticalArrangement = spacedBy(16.dp), diff --git a/next/src/main/kotlin/org/fdroid/ui/repositories/details/BasicAuth.kt b/next/src/main/kotlin/org/fdroid/ui/repositories/details/BasicAuth.kt index 5405dc15f..79e25569b 100644 --- a/next/src/main/kotlin/org/fdroid/ui/repositories/details/BasicAuth.kt +++ b/next/src/main/kotlin/org/fdroid/ui/repositories/details/BasicAuth.kt @@ -20,6 +20,9 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import org.fdroid.R @@ -53,7 +56,12 @@ fun BasicAuth( state = passwordState, label = { Text(stringResource(R.string.repo_basicauth_password)) } ) - AnimatedVisibility(showSaveButton, Modifier.align(Alignment.End)) { + AnimatedVisibility( + visible = showSaveButton, + modifier = Modifier + .align(Alignment.End) + .semantics { liveRegion = LiveRegionMode.Polite }, + ) { FDroidOutlineButton( text = stringResource(R.string.repo_basicauth_edit), onClick = { diff --git a/next/src/main/kotlin/org/fdroid/ui/repositories/details/RepoDetails.kt b/next/src/main/kotlin/org/fdroid/ui/repositories/details/RepoDetails.kt index 7c3770e41..2c5aa3f2a 100644 --- a/next/src/main/kotlin/org/fdroid/ui/repositories/details/RepoDetails.kt +++ b/next/src/main/kotlin/org/fdroid/ui/repositories/details/RepoDetails.kt @@ -29,6 +29,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.viktormykhailiv.compose.hints.rememberHint @@ -115,7 +117,7 @@ fun RepoDetails( IconButton(onClick = { menuExpanded = !menuExpanded }) { Icon( Icons.Default.MoreVert, - contentDescription = null, + contentDescription = stringResource(R.string.more), ) } DropdownMenu( @@ -129,7 +131,11 @@ fun RepoDetails( RepoUpdateWorker.updateNow(context, repo.repoId) }, leadingIcon = { - Icon(Icons.Default.Update, contentDescription = null) + Icon( + imageVector = Icons.Default.Update, + contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, + ) } ) DropdownMenuItem( @@ -139,7 +145,11 @@ fun RepoDetails( deleteDialog = true }, leadingIcon = { - Icon(Icons.Default.Delete, contentDescription = null) + Icon( + imageVector = Icons.Default.Delete, + contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, + ) } ) } diff --git a/next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt b/next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt index 478294700..98cd7b57d 100644 --- a/next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt +++ b/next/src/main/kotlin/org/fdroid/ui/settings/PreferenceProxy.kt @@ -17,6 +17,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import me.zhanghai.compose.preference.ProvidePreferenceLocals @@ -39,6 +41,7 @@ fun LazyListScope.preferenceProxy( Icon( imageVector = Icons.Default.VpnLock, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, title = { diff --git a/next/src/main/kotlin/org/fdroid/ui/settings/Settings.kt b/next/src/main/kotlin/org/fdroid/ui/settings/Settings.kt index 239d67305..fde06cc89 100644 --- a/next/src/main/kotlin/org/fdroid/ui/settings/Settings.kt +++ b/next/src/main/kotlin/org/fdroid/ui/settings/Settings.kt @@ -40,6 +40,8 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalResources import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp @@ -131,6 +133,7 @@ fun Settings( Icon( imageVector = Icons.Default.BrightnessMedium, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, summary = { Text(text = "${themeToString(it)}") }, @@ -141,6 +144,7 @@ fun Settings( Icon( imageVector = Icons.Default.Translate, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, title = { Text(stringResource(R.string.pref_language)) }, @@ -158,6 +162,7 @@ fun Settings( Icon( imageVector = Icons.Default.Notifications, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, title = { Text(stringResource(R.string.notification_title)) }, @@ -183,10 +188,12 @@ fun Settings( if (repoUpdatesEnabled) Icon( imageVector = Icons.Default.SystemSecurityUpdate, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) else Icon( imageVector = Icons.Default.SystemSecurityUpdateWarning, contentDescription = null, tint = MaterialTheme.colorScheme.error, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, summary = { repoUpdatesEnabled -> @@ -231,6 +238,7 @@ fun Settings( Icons.Default.UpdateDisabled }, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) }, summary = { autoUpdatesEnabled -> diff --git a/next/src/main/kotlin/org/fdroid/ui/utils/ExpandIcon.kt b/next/src/main/kotlin/org/fdroid/ui/utils/ExpandIcon.kt new file mode 100644 index 000000000..e4f77f372 --- /dev/null +++ b/next/src/main/kotlin/org/fdroid/ui/utils/ExpandIcon.kt @@ -0,0 +1,41 @@ +package org.fdroid.ui.utils + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ExpandLess +import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material3.Icon +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.fdroid.R + +@Composable +fun ExpandIconChevron(isExpanded: Boolean) { + Icon( + imageVector = if (isExpanded) { + Icons.Default.ExpandLess + } else { + Icons.Default.ExpandMore + }, + contentDescription = if (isExpanded) { + stringResource(R.string.collapse) + } else { + stringResource(R.string.expand) + }, + ) +} + +@Composable +fun ExpandIconArrow(isExpanded: Boolean) { + Icon( + imageVector = if (isExpanded) { + Icons.Default.ExpandLess + } else { + Icons.Default.ExpandMore + }, + contentDescription = if (isExpanded) { + stringResource(R.string.collapse) + } else { + stringResource(R.string.expand) + }, + ) +} diff --git a/next/src/main/kotlin/org/fdroid/ui/utils/ExpandableSection.kt b/next/src/main/kotlin/org/fdroid/ui/utils/ExpandableSection.kt index cfebae557..bb2576b29 100644 --- a/next/src/main/kotlin/org/fdroid/ui/utils/ExpandableSection.kt +++ b/next/src/main/kotlin/org/fdroid/ui/utils/ExpandableSection.kt @@ -7,9 +7,6 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.heightIn -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.KeyboardArrowDown -import androidx.compose.material.icons.filled.KeyboardArrowUp import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -23,6 +20,10 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalInspectionMode +import androidx.compose.ui.semantics.LiveRegionMode +import androidx.compose.ui.semantics.hideFromAccessibility +import androidx.compose.ui.semantics.liveRegion +import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp @Composable @@ -45,6 +46,7 @@ fun ExpandableSection( if (icon != null) Icon( painter = icon, contentDescription = null, + modifier = Modifier.semantics { hideFromAccessibility() }, ) Text( text = title, @@ -52,17 +54,13 @@ fun ExpandableSection( modifier = Modifier.weight(1f), ) IconButton(onClick = { sectionExpanded = !sectionExpanded }) { - Icon( - imageVector = if (sectionExpanded) { - Icons.Default.KeyboardArrowUp - } else { - Icons.Default.KeyboardArrowDown - }, - contentDescription = null, - ) + ExpandIconArrow(sectionExpanded) } } - AnimatedVisibility(sectionExpanded) { + AnimatedVisibility( + visible = sectionExpanded, + modifier = Modifier.semantics { liveRegion = LiveRegionMode.Polite }, + ) { content() } } diff --git a/next/src/main/res/values/strings-next.xml b/next/src/main/res/values/strings-next.xml index f227e4508..78a7568fc 100644 --- a/next/src/main/res/values/strings-next.xml +++ b/next/src/main/res/values/strings-next.xml @@ -94,6 +94,7 @@ Here you can apply filters to the list of apps, e.g. showing only apps within a certain category or repository. Changing the sort order is also possible. Got it Clear + Error Scanning the QR code can only work if you grant camera permission. Tap to grant it in settings. Last update: %s Username