Improve screenreader support

This commit is contained in:
Torsten Grote
2025-11-07 16:42:33 -03:00
parent ba465d147e
commit a4c6febfbc
23 changed files with 192 additions and 65 deletions

View File

@@ -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 {

View File

@@ -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() },
)
}
)
}

View File

@@ -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 = {

View File

@@ -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)
)
}
}

View File

@@ -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 = {

View File

@@ -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(

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 = { },
)

View File

@@ -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,

View File

@@ -144,9 +144,7 @@ fun AppList(
}) {
Icon(
imageVector = Icons.Filled.FilterList,
contentDescription = stringResource(
R.string.search_filter_no_results
),
contentDescription = stringResource(R.string.filter),
)
}
}

View File

@@ -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(

View File

@@ -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)
}

View File

@@ -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),
)

View File

@@ -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),

View File

@@ -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 = {

View File

@@ -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() },
)
}
)
}

View File

@@ -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 = {

View File

@@ -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 ->

View File

@@ -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)
},
)
}

View File

@@ -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()
}
}

View File

@@ -94,6 +94,7 @@
<string name="onboarding_app_list_filter_message">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.</string>
<string name="got_it">Got it</string>
<string name="clear">Clear</string>
<string name="error">Error</string>
<string name="permission_camera_denied">Scanning the QR code can only work if you grant camera permission. Tap to grant it in settings.</string>
<string name="repo_last_update_upstream">Last update: %s</string>
<string name="repo_basicauth_username">Username</string>