From 5c6bcd63610015e1bcb2305f5ecfbd75d555c458 Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Tue, 19 May 2026 16:40:37 +0530 Subject: [PATCH] compose: housekeeping --- .../compose/composable/AuroraListItem.kt | 102 ++++++++++ .../store/compose/composable/BlackListItem.kt | 70 ++----- .../store/compose/composable/CategoryItem.kt | 6 +- .../composable/ContainedLoadingIndicator.kt | 2 +- .../compose/composable/DeviceListItem.kt | 57 +----- .../compose/composable/DispenserListItem.kt | 61 ++---- .../compose/composable/DownloadListItem.kt | 175 ++++------------- .../store/compose/composable/EmptyState.kt | 77 -------- .../compose/composable/FavouriteListItem.kt | 107 +++-------- .../aurora/store/compose/composable/Header.kt | 91 --------- .../aurora/store/compose/composable/Info.kt | 6 +- .../compose/composable/InstallerListItem.kt | 55 +----- .../store/compose/composable/LinkListItem.kt | 74 ++----- .../compose/composable/LocaleListItem.kt | 51 +---- .../aurora/store/compose/composable/Logo.kt | 2 +- .../aurora/store/compose/composable/MicroG.kt | 18 +- .../store/compose/composable/PageIndicator.kt | 4 +- .../compose/composable/PermissionList.kt | 2 +- .../compose/composable/PermissionListItem.kt | 61 ++---- .../composable/{Error.kt => Placeholder.kt} | 42 ++-- .../compose/composable/RemovableListItem.kt | 43 +++++ .../composable/SearchSuggestionListItem.kt | 62 ++---- .../store/compose/composable/SectionHeader.kt | 87 +++------ .../store/compose/composable/Shimmer.kt | 173 +++++++---------- .../compose/composable/StreamCarousel.kt | 25 ++- .../composable/TextDividerComposable.kt | 2 +- .../compose/composable/app/AppListItem.kt | 4 +- .../compose/composable/app/AppUpdateItem.kt | 8 +- .../composable/app/LargeAppListItem.kt | 106 ++++------ .../composable/details/ExodusListItem.kt | 2 +- .../composable/details/RatingListItem.kt | 4 +- .../composable/details/ReviewListItem.kt | 6 +- .../store/compose/ui/about/AboutScreen.kt | 6 +- .../compose/ui/accounts/AccountsScreen.kt | 14 +- .../store/compose/ui/apps/TopChartsPage.kt | 31 +-- .../compose/ui/blacklist/BlacklistScreen.kt | 6 +- .../ui/commons/CategoryBrowseScreen.kt | 4 +- .../ui/commons/ExpandedStreamBrowseScreen.kt | 8 +- .../store/compose/ui/commons/MoreSheet.kt | 12 +- .../store/compose/ui/commons/NetworkSheet.kt | 10 +- .../compose/ui/commons/StreamBrowseScreen.kt | 8 +- .../compose/ui/details/AppDetailsScreen.kt | 11 +- .../store/compose/ui/details/ExodusScreen.kt | 12 +- .../ui/details/ManualDownloadScreen.kt | 8 +- .../store/compose/ui/details/MicroGScreen.kt | 8 +- .../store/compose/ui/details/MoreScreen.kt | 14 +- .../compose/ui/details/PermissionScreen.kt | 6 +- .../store/compose/ui/details/ReviewScreen.kt | 8 +- .../compose/ui/details/composable/Actions.kt | 6 +- .../ui/details/composable/Changelog.kt | 12 +- .../ui/details/composable/Compatibility.kt | 6 +- .../ui/details/composable/DataSafety.kt | 6 +- .../compose/ui/details/composable/Details.kt | 6 +- .../ui/details/composable/DeveloperDetails.kt | 6 +- .../compose/ui/details/composable/Privacy.kt | 6 +- .../ui/details/composable/RatingAndReviews.kt | 18 +- .../ui/details/composable/Screenshots.kt | 6 +- .../compose/ui/details/composable/Tags.kt | 6 +- .../compose/ui/details/composable/Testing.kt | 10 +- .../store/compose/ui/dev/DevProfileScreen.kt | 8 +- .../compose/ui/dispenser/DispenserScreen.kt | 8 +- .../ui/dispenser/InputDispenserDialog.kt | 4 +- .../compose/ui/downloads/DownloadsScreen.kt | 30 +-- .../compose/ui/favourite/FavouriteScreen.kt | 6 +- .../compose/ui/installed/InstalledScreen.kt | 6 +- .../store/compose/ui/onboarding/MicroGPage.kt | 4 +- .../compose/ui/onboarding/OnboardingScreen.kt | 8 +- .../compose/ui/onboarding/PermissionsPage.kt | 4 +- .../compose/ui/onboarding/WelcomePage.kt | 6 +- .../installation/InstallerScreen.kt | 2 +- .../network/NetworkPreferenceScreen.kt | 8 +- .../updates/UpdatesPreferenceScreen.kt | 2 +- .../store/compose/ui/search/SearchScreen.kt | 16 +- .../store/compose/ui/sheets/AppUpdateSheet.kt | 26 +-- .../compose/ui/sheets/DownloadActionsSheet.kt | 181 ++++++++++++++++++ .../store/compose/ui/splash/SplashScreen.kt | 4 +- .../store/compose/ui/spoof/DevicePage.kt | 2 +- .../store/compose/ui/spoof/LocalePage.kt | 2 +- .../store/compose/ui/updates/UpdatesScreen.kt | 53 ++--- app/src/main/res/drawable/bg_changelog.xml | 8 +- app/src/main/res/drawable/divider.xml | 4 +- app/src/main/res/drawable/tab_indicator.xml | 2 +- app/src/main/res/values/dimens.xml | 23 +-- 83 files changed, 940 insertions(+), 1316 deletions(-) create mode 100644 app/src/main/java/com/aurora/store/compose/composable/AuroraListItem.kt delete mode 100644 app/src/main/java/com/aurora/store/compose/composable/EmptyState.kt delete mode 100644 app/src/main/java/com/aurora/store/compose/composable/Header.kt rename app/src/main/java/com/aurora/store/compose/composable/{Error.kt => Placeholder.kt} (64%) create mode 100644 app/src/main/java/com/aurora/store/compose/composable/RemovableListItem.kt create mode 100644 app/src/main/java/com/aurora/store/compose/ui/sheets/DownloadActionsSheet.kt diff --git a/app/src/main/java/com/aurora/store/compose/composable/AuroraListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/AuroraListItem.kt new file mode 100644 index 000000000..b89d05413 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/composable/AuroraListItem.kt @@ -0,0 +1,102 @@ +/* + * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.composable + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewWrapper +import com.aurora.store.R +import com.aurora.store.compose.preview.ThemePreviewProvider + +/** + * Single shared layout for every list-row in the app. A leading slot (icon, image…), a + * stacked headline + supporting + tertiary text column, and a trailing slot (checkbox, + * button, chip…). [headlineStyle] toggles between data-row (bodyMedium) and settings-row + * (bodyLarge) typography while keeping every other dimension consistent. + */ +@Composable +fun AuroraListItem( + modifier: Modifier = Modifier, + headline: String, + supporting: String? = null, + tertiary: String? = null, + onClick: (() -> Unit)? = null, + enabled: Boolean = true, + leading: (@Composable () -> Unit)? = null, + trailing: (@Composable () -> Unit)? = null, + headlineStyle: TextStyle = MaterialTheme.typography.bodyLarge +) { + Row( + modifier = modifier + .fillMaxWidth() + .then( + if (onClick != null) { + Modifier.clickable(enabled = enabled, onClick = onClick) + } else { + Modifier + } + ) + .padding( + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) + ), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)), + verticalAlignment = Alignment.CenterVertically + ) { + if (leading != null) leading() + Column(modifier = Modifier.weight(1f)) { + Text( + text = headline, + style = headlineStyle, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + if (!supporting.isNullOrBlank()) { + Text( + text = supporting, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + if (!tertiary.isNullOrBlank()) { + Text( + text = tertiary, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + if (trailing != null) trailing() + } +} + +@PreviewWrapper(ThemePreviewProvider::class) +@Preview(showBackground = true) +@Composable +private fun AuroraListItemPreview() { + AuroraListItem( + headline = "Example app", + supporting = "com.example.app", + tertiary = "v1.0.0 (100)" + ) +} diff --git a/app/src/main/java/com/aurora/store/compose/composable/BlackListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/BlackListItem.kt index 03960dce2..7e5f57f1c 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/BlackListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/BlackListItem.kt @@ -8,23 +8,14 @@ package com.aurora.store.compose.composable import android.graphics.Bitmap import android.graphics.Color import androidx.compose.foundation.Image -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.material3.Checkbox import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.core.graphics.drawable.toBitmap @@ -33,18 +24,6 @@ import com.aurora.store.BuildConfig import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider -/** - * Composable for displaying package details in a list for blacklisting - * @param modifier The modifier to be applied to the composable - * @param icon Icon for the package - * @param displayName User-readable name of the package - * @param packageName Name of the package - * @param versionName versionName of the package - * @param versionCode versionCode of the package - * @param isChecked Whether the app is blacklisted - * @param isEnabled Whether this app is allowed to be blacklisted - * @param onClick Callback when the composable is clicked - */ @Composable fun BlackListItem( modifier: Modifier = Modifier, @@ -57,48 +36,25 @@ fun BlackListItem( isEnabled: Boolean = true, onClick: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(enabled = isEnabled, onClick = onClick) - .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) - ), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row(modifier = Modifier.weight(1F)) { + AuroraListItem( + modifier = modifier, + headline = displayName, + supporting = packageName, + tertiary = stringResource(R.string.version, versionName, versionCode), + headlineStyle = MaterialTheme.typography.bodyMedium, + onClick = onClick, + enabled = isEnabled, + leading = { Image( bitmap = icon.asImageBitmap(), contentDescription = null, modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)) ) - Column( - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)) - ) { - Text( - text = displayName, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = packageName, - style = MaterialTheme.typography.bodySmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = stringResource(R.string.version, versionName, versionCode), - style = MaterialTheme.typography.bodySmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } + }, + trailing = { + Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() }) } - Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() }) - } + ) } @PreviewWrapper(ThemePreviewProvider::class) diff --git a/app/src/main/java/com/aurora/store/compose/composable/CategoryItem.kt b/app/src/main/java/com/aurora/store/compose/composable/CategoryItem.kt index d891f33d0..95167ad99 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/CategoryItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/CategoryItem.kt @@ -38,11 +38,11 @@ fun CategoryItem(modifier: Modifier = Modifier, category: Category, onClick: () .fillMaxWidth() .clickable(onClick = onClick) .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large)) ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) diff --git a/app/src/main/java/com/aurora/store/compose/composable/ContainedLoadingIndicator.kt b/app/src/main/java/com/aurora/store/compose/composable/ContainedLoadingIndicator.kt index 0d292e823..c4788c2a4 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/ContainedLoadingIndicator.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/ContainedLoadingIndicator.kt @@ -31,7 +31,7 @@ fun ContainedLoadingIndicator(modifier: Modifier = Modifier) { Box( modifier = modifier .fillMaxSize() - .padding(dimensionResource(R.dimen.padding_small)), + .padding(dimensionResource(R.dimen.spacing_small)), contentAlignment = Alignment.Center ) { val description = stringResource(R.string.loading) diff --git a/app/src/main/java/com/aurora/store/compose/composable/DeviceListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/DeviceListItem.kt index 0fb05fbd8..5ee652fe2 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/DeviceListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/DeviceListItem.kt @@ -5,34 +5,13 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Checkbox -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.aurora.store.R -/** - * Composable to display device details for spoofing in a list - * @param modifier The modifier to be applied to the composable - * @param userReadableName Name of the device, obtained through `UserReadableName` property - * @param manufacturer Name of the device manufacturer, obtained through `Build.MANUFACTURER` property - * @param androidVersionSdk Android version on the device, obtained through `Build.VERSION.SDK_INT` property - * @param platforms Platforms supported on the device, obtained through `Platforms` property - * @param isChecked If the device is selected - * @param onClick Callback when the composable is clicked - */ @Composable fun DeviceListItem( modifier: Modifier = Modifier, @@ -43,34 +22,16 @@ fun DeviceListItem( isChecked: Boolean = false, onClick: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = { if (!isChecked) onClick() }) - .padding(dimensionResource(R.dimen.padding_small)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column(modifier = Modifier.weight(1F)) { - Text( - text = userReadableName, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk), - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = platforms.replace(",\\s*".toRegex(), ", "), - style = MaterialTheme.typography.bodySmall - ) + AuroraListItem( + modifier = modifier, + headline = userReadableName, + supporting = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk), + tertiary = platforms.replace(",\\s*".toRegex(), ", "), + onClick = { if (!isChecked) onClick() }, + trailing = { + Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) } - Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) - } + ) } @Preview(showBackground = true) diff --git a/app/src/main/java/com/aurora/store/compose/composable/DispenserListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/DispenserListItem.kt index 20dd4cd57..8b663c2c6 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/DispenserListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/DispenserListItem.kt @@ -5,32 +5,17 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import com.aurora.store.R -/** - * Composable to display dispenser URL in a list - * @param modifier The modifier to be applied to the composable - * @param url URL of the dispenser - * @param onClick Callback when this URL is clicked - * @param onClear Callback when the clear button is clicked - */ @Composable fun DispenserListItem( modifier: Modifier = Modifier, @@ -38,37 +23,23 @@ fun DispenserListItem( onClick: () -> Unit = {}, onClear: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding(dimensionResource(R.dimen.padding_small)), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Row( - modifier = Modifier.weight(1F), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) - ) { - Icon( - painter = painterResource(R.drawable.ic_server), - contentDescription = null - ) - Text( - text = url, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + AuroraListItem( + modifier = modifier, + headline = url, + onClick = onClick, + leading = { + Icon(painter = painterResource(R.drawable.ic_server), contentDescription = null) + }, + trailing = { + TextButton(onClick = onClear) { + Text( + text = stringResource(R.string.remove), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } } - TextButton(onClick = onClear) { - Text( - text = stringResource(R.string.remove), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - } + ) } @Preview(showBackground = true) diff --git a/app/src/main/java/com/aurora/store/compose/composable/DownloadListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/DownloadListItem.kt index 216004a32..5948e8873 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/DownloadListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/DownloadListItem.kt @@ -8,36 +8,16 @@ package com.aurora.store.compose.composable import android.text.format.DateUtils import android.text.format.Formatter -import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize -import androidx.compose.material3.DropdownMenu -import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -49,138 +29,53 @@ import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.model.DownloadStatus import com.aurora.store.data.room.download.Download import com.aurora.store.util.CommonUtil.getETAString -import com.aurora.store.util.PackageUtil -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -/** - * Composable to display details of a download in a list - * @param modifier The modifier to be applied to the composable - * @param download [Download] to display - * @param onClick Callback when this composable is clicked - */ @Composable -fun DownloadListItem( - modifier: Modifier = Modifier, - download: Download, - onClick: () -> Unit = {}, - onClear: () -> Unit = {}, - onCancel: () -> Unit = {}, - onExport: () -> Unit = {}, - onInstall: () -> Unit = {}, - isExpanded: Boolean = false -) { - val progress = "${download.progress}%" - val speed = "${Formatter.formatShortFileSize(LocalContext.current, download.speed)}/s" - val eta = getETAString(LocalContext.current, download.timeRemaining) +fun DownloadListItem(modifier: Modifier = Modifier, download: Download, onClick: () -> Unit = {}) { + val context = LocalContext.current - val coroutineScope = rememberCoroutineScope() - var isVisible by remember { mutableStateOf(true) } - var isExpanded by remember { mutableStateOf(isExpanded) } - - fun requestClear() { - coroutineScope.launch { - isVisible = false - delay(300) // Let the animation play - onClear() - } + val statusLine = stringResource(download.status.localized) + val tertiaryLine = if (download.status in DownloadStatus.running) { + val progress = "${download.progress}%" + val speed = "${Formatter.formatShortFileSize(context, download.speed)}/s" + val eta = getETAString(context, download.timeRemaining) + "$progress • $speed • $eta" + } else { + DateUtils.formatDateTime(context, download.downloadedAt, DateUtils.FORMAT_SHOW_DATE) } - @Composable - fun ExpandedMenu() { - val context = LocalContext.current - Box(modifier = modifier) { - IconButton(onClick = { isExpanded = true }) { + AuroraListItem( + modifier = modifier, + headline = download.displayName, + supporting = statusLine, + tertiary = tertiaryLine, + headlineStyle = MaterialTheme.typography.bodyMedium, + onClick = onClick, + leading = { + AnimatedAppIcon( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)), + iconUrl = download.iconURL, + inProgress = download.isRunning, + progress = download.progress.toFloat() + ) + }, + trailing = if (download.isFinished) { + { Icon( - painter = painterResource(R.drawable.ic_more_vert), - contentDescription = stringResource(R.string.menu) - ) - } - DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) { - DropdownMenuItem( - text = { Text(text = stringResource(R.string.action_cancel)) }, - onClick = onCancel, - enabled = download.isRunning - ) - DropdownMenuItem( - text = { Text(text = stringResource(R.string.action_install)) }, - onClick = onInstall, - enabled = download.canInstall(context) - ) - DropdownMenuItem( - text = { Text(text = stringResource(R.string.action_export)) }, - onClick = onExport, - enabled = download.canInstall(context) || - PackageUtil.isInstalled( - context, - download.packageName - ) - ) - DropdownMenuItem( - text = { Text(text = stringResource(R.string.action_clear)) }, - onClick = ::requestClear, - enabled = !download.isRunning + painter = painterResource(R.drawable.ic_check), + contentDescription = stringResource(R.string.download_completed), + tint = Color(0xFF4CAF50) ) } + } else { + null } - } - - AnimatedVisibility(visible = isVisible, exit = shrinkVertically() + fadeOut()) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) - ), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row(modifier = Modifier.weight(1F)) { - AnimatedAppIcon( - modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)), - iconUrl = download.iconURL, - inProgress = download.isRunning, - progress = download.progress.toFloat() - ) - Column( - modifier = Modifier - .padding(horizontal = dimensionResource(R.dimen.margin_small)) - ) { - Text( - text = download.displayName, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = stringResource(download.status.localized), - style = MaterialTheme.typography.bodySmall - ) - AnimatedContent(targetState = download.status) { status -> - Text( - style = MaterialTheme.typography.bodySmall, - text = if (status in DownloadStatus.running) { - "$progress • $speed • $eta" - } else { - DateUtils.formatDateTime( - LocalContext.current, - download.downloadedAt, - DateUtils.FORMAT_SHOW_DATE - ).toString() - } - ) - } - } - } - ExpandedMenu() - } - } + ) } @PreviewWrapper(ThemePreviewProvider::class) @Preview(showBackground = true) @Composable private fun DownloadListItemPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { - DownloadListItem(download = Download.fromApp(app), isExpanded = true) + DownloadListItem(download = Download.fromApp(app)) } diff --git a/app/src/main/java/com/aurora/store/compose/composable/EmptyState.kt b/app/src/main/java/com/aurora/store/compose/composable/EmptyState.kt deleted file mode 100644 index 8e973ccbd..000000000 --- a/app/src/main/java/com/aurora/store/compose/composable/EmptyState.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Aurora OSS - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.aurora.store.compose.composable - -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.size -import androidx.compose.material3.FilledTonalButton -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewWrapper -import com.aurora.store.R -import com.aurora.store.compose.preview.ThemePreviewProvider - -@Composable -fun EmptyState( - modifier: Modifier = Modifier, - @DrawableRes icon: Int, - @StringRes message: Int, - @StringRes actionLabel: Int? = null, - onAction: () -> Unit = {} -) { - Column( - modifier = modifier.fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_small), - Alignment.CenterVertically - ) - ) { - Icon( - painter = painterResource(icon), - contentDescription = null, - modifier = Modifier.size(dimensionResource(R.dimen.icon_size_medium)), - tint = MaterialTheme.colorScheme.primary - ) - - Text( - text = stringResource(message), - style = MaterialTheme.typography.bodyLarge, - textAlign = TextAlign.Center, - color = MaterialTheme.colorScheme.onPrimary - ) - - if (actionLabel != null) { - FilledTonalButton(onClick = onAction) { - Text(stringResource(actionLabel)) - } - } - } -} - -@PreviewWrapper(ThemePreviewProvider::class) -@Preview -@Composable -private fun EmptyStatePreview() { - EmptyState( - icon = R.drawable.ic_updates, - message = R.string.details_no_updates, - actionLabel = R.string.check_updates - ) -} diff --git a/app/src/main/java/com/aurora/store/compose/composable/FavouriteListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/FavouriteListItem.kt index ac57d8759..7e357fa12 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/FavouriteListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/FavouriteListItem.kt @@ -6,28 +6,12 @@ package com.aurora.store.compose.composable import android.text.format.DateUtils -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.fadeOut -import androidx.compose.animation.shrinkVertically -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -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.layout.ContentScale @@ -35,7 +19,6 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -47,16 +30,7 @@ import com.aurora.store.R import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.room.favourite.Favourite -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -/** - * Composable to display a favourite app in a list - * @param modifier The modifier to be applied to the composable - * @param favourite A [Favourite] app to display - * @param onClick Callback when this composable is clicked - * @param onClear Callback when the favourite button is clicked to remove the app from favourites - */ @Composable fun FavouriteListItem( modifier: Modifier = Modifier, @@ -64,35 +38,22 @@ fun FavouriteListItem( onClick: () -> Unit = {}, onClear: () -> Unit = {} ) { - val coroutineScope = rememberCoroutineScope() - var isVisible by remember { mutableStateOf(true) } - - fun requestClear() { - coroutineScope.launch { - isVisible = false - delay(300) // Let the animation play - onClear() - } - } - - AnimatedVisibility( - visible = isVisible, - exit = shrinkVertically() + fadeOut() - ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) - ), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row(modifier = Modifier.weight(1F)) { + val context = LocalContext.current + RemovableListItem(onRemove = onClear) { triggerRemove -> + AuroraListItem( + modifier = modifier, + headline = favourite.displayName, + supporting = favourite.packageName, + tertiary = DateUtils.formatDateTime( + context, + favourite.added, + DateUtils.FORMAT_SHOW_DATE + ), + headlineStyle = MaterialTheme.typography.bodyMedium, + onClick = onClick, + leading = { AsyncImage( - model = ImageRequest.Builder(LocalContext.current) + model = ImageRequest.Builder(context) .data(favourite.iconURL) .crossfade(true) .build(), @@ -102,40 +63,16 @@ fun FavouriteListItem( .requiredSize(dimensionResource(R.dimen.icon_size_medium)) .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) ) - Column( - modifier = Modifier.padding( - horizontal = dimensionResource(R.dimen.margin_small) - ) - ) { - Text( - text = favourite.displayName, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = favourite.packageName, - style = MaterialTheme.typography.bodySmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = DateUtils.formatDateTime( - LocalContext.current, - favourite.added, - DateUtils.FORMAT_SHOW_DATE - ), - style = MaterialTheme.typography.bodySmall + }, + trailing = { + IconButton(onClick = triggerRemove) { + Icon( + painter = painterResource(R.drawable.ic_favorite_checked), + contentDescription = stringResource(R.string.action_favourite) ) } } - IconButton(onClick = { requestClear() }) { - Icon( - painter = painterResource(R.drawable.ic_favorite_checked), - contentDescription = stringResource(R.string.action_favourite) - ) - } - } + ) } } diff --git a/app/src/main/java/com/aurora/store/compose/composable/Header.kt b/app/src/main/java/com/aurora/store/compose/composable/Header.kt deleted file mode 100644 index 1bf179455..000000000 --- a/app/src/main/java/com/aurora/store/compose/composable/Header.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2026 Aurora OSS - * SPDX-FileCopyrightText: 2025 The Calyx Institute - * SPDX-License-Identifier: GPL-3.0-or-later - */ - -package com.aurora.store.compose.composable - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.tooling.preview.PreviewWrapper -import com.aurora.store.R -import com.aurora.store.compose.preview.ThemePreviewProvider - -/** - * Composable to display sticky header in list - * @param modifier The modifier to be applied to the composable - * @param title Title to display - * @param subtitle Optional subtitle to display - * @param onClick Callback when this composable is clicked - * @see TextDividerComposable - */ -@Composable -fun Header( - modifier: Modifier = Modifier, - title: String, - subtitle: String? = null, - onClick: (() -> Unit)? = null -) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) - .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) - ), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Column( - modifier = Modifier.weight(1F) - ) { - Text( - text = title, - style = MaterialTheme.typography.titleMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - if (!subtitle.isNullOrBlank()) { - Text( - text = subtitle, - style = MaterialTheme.typography.bodyMedium, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } - if (onClick != null) { - Icon( - painter = painterResource(R.drawable.ic_arrow_right), - contentDescription = null - ) - } - } -} - -@PreviewWrapper(ThemePreviewProvider::class) -@Preview(showBackground = true) -@Composable -private fun HeaderPreview() { - Header( - title = stringResource(R.string.details_privacy), - subtitle = stringResource(R.string.exodus_powered), - onClick = {} - ) -} diff --git a/app/src/main/java/com/aurora/store/compose/composable/Info.kt b/app/src/main/java/com/aurora/store/compose/composable/Info.kt index 05ddee912..448d6d96a 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/Info.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/Info.kt @@ -57,10 +57,10 @@ fun Info( .fillMaxWidth() .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xxsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)), verticalAlignment = Alignment.CenterVertically ) { if (painter != null) Icon(painter = painter, contentDescription = null) diff --git a/app/src/main/java/com/aurora/store/compose/composable/InstallerListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/InstallerListItem.kt index 449a9baa7..ecf4fa97f 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/InstallerListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/InstallerListItem.kt @@ -5,35 +5,16 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper -import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.installer.SessionInstaller import com.aurora.store.data.model.InstallerInfo -/** - * Composable to display installer details in a list - * @param modifier The modifier to be applied to the composable - * @param installerInfo A [InstallerInfo] object to display details - * @param isSelected Whether this installer is selected - * @param onClick Callback when this composable is clicked - */ @Composable fun InstallerListItem( modifier: Modifier = Modifier, @@ -41,34 +22,14 @@ fun InstallerListItem( isSelected: Boolean = false, onClick: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding(dimensionResource(R.dimen.padding_small)), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Column(modifier = Modifier.weight(1F)) { - Text( - text = stringResource(installerInfo.title), - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = stringResource(installerInfo.subtitle), - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = stringResource(installerInfo.description), - style = MaterialTheme.typography.bodySmall - ) - } - RadioButton(selected = isSelected, onClick = onClick) - } + AuroraListItem( + modifier = modifier, + headline = stringResource(installerInfo.title), + supporting = stringResource(installerInfo.subtitle), + tertiary = stringResource(installerInfo.description), + onClick = onClick, + trailing = { RadioButton(selected = isSelected, onClick = onClick) } + ) } @PreviewWrapper(ThemePreviewProvider::class) diff --git a/app/src/main/java/com/aurora/store/compose/composable/LinkListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/LinkListItem.kt index b6869af4c..443950ec5 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/LinkListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/LinkListItem.kt @@ -6,21 +6,9 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize -import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.VerticalDivider import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color @@ -29,7 +17,6 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import coil3.compose.AsyncImage @@ -38,12 +25,6 @@ import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.model.Link -/** - * Composable to show link details in a list - * @param modifier The modifier to be applied to the composable - * @param link [Link] to show details - * @param onClick Callback when the composable is clicked - */ @Composable fun LinkListItem( modifier: Modifier = Modifier, @@ -51,46 +32,25 @@ fun LinkListItem( onClick: () -> Unit = {}, iconTint: Color? = null ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding(dimensionResource(R.dimen.padding_small)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) - ) { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(link.icon) - .build(), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_default)) - .clip(CircleShape), - colorFilter = if (iconTint != null) ColorFilter.tint(color = iconTint) else null - ) - VerticalDivider( - modifier = Modifier - .padding(horizontal = dimensionResource(R.dimen.margin_large)) - .width(dimensionResource(R.dimen.padding_xxsmall)) - .height(dimensionResource(R.dimen.height_header)) - ) - Column { - Text( - text = link.title, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = link.subtitle, - style = MaterialTheme.typography.bodySmall, - maxLines = 2, - overflow = TextOverflow.Ellipsis + AuroraListItem( + modifier = modifier, + headline = link.title, + supporting = link.subtitle, + onClick = onClick, + leading = { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(link.icon) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .requiredSize(dimensionResource(R.dimen.icon_size_default)) + .clip(CircleShape), + colorFilter = if (iconTint != null) ColorFilter.tint(color = iconTint) else null ) } - } + ) } @PreviewWrapper(ThemePreviewProvider::class) diff --git a/app/src/main/java/com/aurora/store/compose/composable/LocaleListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/LocaleListItem.kt index cebc413d8..63d8a299f 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/LocaleListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/LocaleListItem.kt @@ -5,32 +5,12 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.material3.Checkbox -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import com.aurora.store.R import java.util.Locale -/** - * Composable to display locale details in a list - * @param modifier The modifier to be applied to the composable - * @param displayName Display name of the locale - * @param displayLanguage Display name of the language in the locale - * @param isChecked Whether the locale is checked/selected - * @param onClick Callback when the composable is clicked - */ @Composable fun LocaleListItem( modifier: Modifier = Modifier, @@ -39,30 +19,15 @@ fun LocaleListItem( isChecked: Boolean = false, onClick: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = { if (!isChecked) onClick() }) - .padding(dimensionResource(R.dimen.padding_small)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column(modifier = Modifier.weight(1F)) { - Text( - text = displayName, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = displayLanguage, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + AuroraListItem( + modifier = modifier, + headline = displayName, + supporting = displayLanguage, + onClick = { if (!isChecked) onClick() }, + trailing = { + Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) } - Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) - } + ) } @Preview(showBackground = true) diff --git a/app/src/main/java/com/aurora/store/compose/composable/Logo.kt b/app/src/main/java/com/aurora/store/compose/composable/Logo.kt index ad51a8658..e66384e19 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/Logo.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/Logo.kt @@ -36,7 +36,7 @@ fun Logo(modifier: Modifier = Modifier) { Column( modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_small), + dimensionResource(R.dimen.spacing_small), Alignment.CenterVertically ), horizontalAlignment = Alignment.CenterHorizontally diff --git a/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt b/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt index cd5cb855a..188efd7cb 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt @@ -91,13 +91,13 @@ fun MicroG( modifier = modifier .fillMaxSize() .padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_xxsmall) + horizontal = dimensionResource(R.dimen.spacing_small), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), verticalArrangement = Arrangement.SpaceBetween ) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { Text( text = stringResource(R.string.onboarding_gms_microg), @@ -116,8 +116,8 @@ fun MicroG( Column( modifier = Modifier .fillMaxWidth() - .padding(vertical = dimensionResource(R.dimen.padding_large)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + .padding(vertical = dimensionResource(R.dimen.spacing_large)), + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { uiState.items.fastForEach { item -> MicroGItemRow(item = item) @@ -127,7 +127,7 @@ fun MicroG( Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.padding_small) + dimensionResource(R.dimen.spacing_small) ) ) { Checkbox( @@ -218,8 +218,8 @@ private fun MicroGItemRow(item: ExternalItem) { modifier = Modifier .fillMaxWidth() .padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_xsmall) + horizontal = dimensionResource(R.dimen.spacing_small), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), verticalAlignment = Alignment.CenterVertically ) { @@ -232,7 +232,7 @@ private fun MicroGItemRow(item: ExternalItem) { Column( modifier = Modifier .weight(1F) - .padding(horizontal = dimensionResource(R.dimen.margin_small)) + .padding(horizontal = dimensionResource(R.dimen.spacing_small)) ) { Text( text = item.displayName, diff --git a/app/src/main/java/com/aurora/store/compose/composable/PageIndicator.kt b/app/src/main/java/com/aurora/store/compose/composable/PageIndicator.kt index 5793ca70a..680015acb 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/PageIndicator.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/PageIndicator.kt @@ -43,7 +43,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I modifier = modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_xsmall), + dimensionResource(R.dimen.spacing_xsmall), Alignment.CenterHorizontally ) ) { @@ -60,7 +60,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I ) val size by animateDpAsState( targetValue = if (isSelected) { - dimensionResource(R.dimen.radius_normal) + dimensionResource(R.dimen.radius_medium) } else { dimensionResource(R.dimen.radius_small) }, diff --git a/app/src/main/java/com/aurora/store/compose/composable/PermissionList.kt b/app/src/main/java/com/aurora/store/compose/composable/PermissionList.kt index c428b82af..d7d96de19 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/PermissionList.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/PermissionList.kt @@ -133,7 +133,7 @@ fun PermissionList( LazyColumn( modifier = modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { if (header != null) stickyHeader(content = header) diff --git a/app/src/main/java/com/aurora/store/compose/composable/PermissionListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/PermissionListItem.kt index d5c941756..0bfec9df5 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/PermissionListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/PermissionListItem.kt @@ -5,18 +5,10 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -26,49 +18,30 @@ import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.model.Permission import com.aurora.store.data.model.PermissionType -/** - * Composable to display permission details in a list - * @param modifier The modifier to be applied to the composable - * @param permission [Permission] to display - * @param onAction Callback when the user clicks the action button - */ @Composable fun PermissionListItem( modifier: Modifier = Modifier, permission: Permission, onAction: () -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_small)), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column(modifier = Modifier.weight(1F)) { - Text( - text = permission.title, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - text = permission.subtitle, - style = MaterialTheme.typography.bodySmall - ) + AuroraListItem( + modifier = modifier, + headline = permission.title, + supporting = permission.subtitle, + trailing = { + TextButton(onClick = onAction, enabled = !permission.isGranted) { + Text( + text = if (permission.isGranted) { + stringResource(R.string.action_granted) + } else { + stringResource(R.string.action_grant) + }, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } } - TextButton(onClick = onAction, enabled = !permission.isGranted) { - Text( - text = if (permission.isGranted) { - stringResource(R.string.action_granted) - } else { - stringResource(R.string.action_grant) - }, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } - } + ) } @PreviewWrapper(ThemePreviewProvider::class) diff --git a/app/src/main/java/com/aurora/store/compose/composable/Error.kt b/app/src/main/java/com/aurora/store/compose/composable/Placeholder.kt similarity index 64% rename from app/src/main/java/com/aurora/store/compose/composable/Error.kt rename to app/src/main/java/com/aurora/store/compose/composable/Placeholder.kt index 47199e886..9abf5c163 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/Error.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/Placeholder.kt @@ -1,4 +1,5 @@ /* + * SPDX-FileCopyrightText: 2026 Aurora OSS * SPDX-FileCopyrightText: 2025 The Calyx Institute * SPDX-License-Identifier: GPL-3.0-or-later */ @@ -8,11 +9,11 @@ package com.aurora.store.compose.composable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredSize -import androidx.compose.material3.Button +import androidx.compose.foundation.layout.size +import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment @@ -29,27 +30,23 @@ import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider /** - * Composable to show error message that fills all available screen - * @param modifier The modifier to be applied to the composable - * @param painter Painter to draw the icon - * @param message Message for error - * @param actionMessage Message to show on action button; defaults to null with button not visible - * @param onAction Callback when action button is clicked + * Full-screen empty/error placeholder: centered icon + message and an optional action button. + * Replaces the previous EmptyState and Error composables. */ @Composable -fun Error( +fun Placeholder( modifier: Modifier = Modifier, painter: Painter, message: String, - actionMessage: String? = null, + actionLabel: String? = null, onAction: (() -> Unit)? = null ) { Column( modifier = modifier .fillMaxSize() - .padding(dimensionResource(R.dimen.padding_medium)), + .padding(dimensionResource(R.dimen.spacing_medium)), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_small), + dimensionResource(R.dimen.spacing_small), Alignment.CenterVertically ), horizontalAlignment = Alignment.CenterHorizontally @@ -57,18 +54,18 @@ fun Error( Icon( painter = painter, contentDescription = null, - modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size)) + modifier = Modifier.size(dimensionResource(R.dimen.icon_size_medium)), + tint = MaterialTheme.colorScheme.primary ) Text( - modifier = Modifier.fillMaxWidth(), text = message, + style = MaterialTheme.typography.bodyLarge, textAlign = TextAlign.Center ) - - if (actionMessage != null) { - Button(onClick = { if (onAction != null) onAction() }, enabled = onAction != null) { + if (actionLabel != null && onAction != null) { + FilledTonalButton(onClick = onAction) { Text( - text = actionMessage, + text = actionLabel, maxLines = 1, overflow = TextOverflow.Ellipsis ) @@ -80,10 +77,11 @@ fun Error( @PreviewWrapper(ThemePreviewProvider::class) @Preview(showBackground = true) @Composable -private fun ErrorPreview() { - Error( +private fun PlaceholderPreview() { + Placeholder( painter = painterResource(R.drawable.ic_updates), message = stringResource(R.string.details_no_updates), - actionMessage = stringResource(R.string.check_updates) + actionLabel = stringResource(R.string.check_updates), + onAction = {} ) } diff --git a/app/src/main/java/com/aurora/store/compose/composable/RemovableListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/RemovableListItem.kt new file mode 100644 index 000000000..60d104967 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/composable/RemovableListItem.kt @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.composable + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeOut +import androidx.compose.animation.shrinkVertically +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +private const val REMOVE_ANIM_DURATION_MS = 300L + +/** + * Wraps [content] in an AnimatedVisibility container that shrinks vertically and fades out + * before invoking [onRemove]. The content lambda receives a trigger callback that, when + * invoked, plays the exit animation and then calls [onRemove]. + */ +@Composable +fun RemovableListItem( + onRemove: () -> Unit, + content: @Composable (triggerRemove: () -> Unit) -> Unit +) { + val scope = rememberCoroutineScope() + var visible by remember { mutableStateOf(true) } + AnimatedVisibility(visible = visible, exit = shrinkVertically() + fadeOut()) { + content { + scope.launch { + visible = false + delay(REMOVE_ANIM_DURATION_MS) + onRemove() + } + } + } +} diff --git a/app/src/main/java/com/aurora/store/compose/composable/SearchSuggestionListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/SearchSuggestionListItem.kt index 8a95d4784..959b07ea6 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/SearchSuggestionListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/SearchSuggestionListItem.kt @@ -5,24 +5,15 @@ package com.aurora.store.compose.composable -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.material3.Icon import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import coil3.compose.AsyncImage @@ -32,13 +23,6 @@ import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider -/** - * Composable for displaying search suggestions in a list - * @param modifier The modifier to be applied to the composable - * @param searchSuggestEntry A [SearchSuggestEntry] to display search suggestion - * @param onClick Callback when this composable is clicked - * @param onAction Callback when action button is clicked - */ @Composable fun SearchSuggestionListItem( modifier: Modifier = Modifier, @@ -46,25 +30,11 @@ fun SearchSuggestionListItem( onClick: (query: String) -> Unit = {}, onAction: (query: String) -> Unit = {} ) { - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = { onClick(searchSuggestEntry.title) }) - .padding( - top = dimensionResource(R.dimen.padding_xsmall), - bottom = dimensionResource(R.dimen.padding_xsmall), - start = dimensionResource(R.dimen.padding_large), - end = dimensionResource(R.dimen.padding_xsmall) - ), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Row( - modifier = Modifier - .weight(1F) - .clickable(onClick = { onClick(searchSuggestEntry.title) }), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) - ) { + AuroraListItem( + modifier = modifier, + headline = searchSuggestEntry.title, + onClick = { onClick(searchSuggestEntry.title) }, + leading = { AsyncImage( model = ImageRequest.Builder(LocalContext.current) .data( @@ -81,20 +51,16 @@ fun SearchSuggestionListItem( contentScale = ContentScale.Crop, modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_default)) ) - Text( - text = searchSuggestEntry.title, - style = MaterialTheme.typography.bodyLarge, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + }, + trailing = { + IconButton(onClick = { onAction(searchSuggestEntry.title) }) { + Icon( + painter = painterResource(R.drawable.ic_search_append), + contentDescription = null + ) + } } - IconButton(onClick = { onAction(searchSuggestEntry.title) }) { - Icon( - painter = painterResource(R.drawable.ic_search_append), - contentDescription = null - ) - } - } + ) } @PreviewWrapper(ThemePreviewProvider::class) diff --git a/app/src/main/java/com/aurora/store/compose/composable/SectionHeader.kt b/app/src/main/java/com/aurora/store/compose/composable/SectionHeader.kt index 27693c53a..9957c1d7b 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/SectionHeader.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/SectionHeader.kt @@ -1,18 +1,18 @@ /* * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-FileCopyrightText: 2025 The Calyx Institute * SPDX-License-Identifier: GPL-3.0-or-later */ package com.aurora.store.compose.composable import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text @@ -22,7 +22,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -30,71 +29,33 @@ import com.aurora.store.R import com.aurora.store.compose.preview.ThemePreviewProvider /** - * Section header row with optional right-chevron for navigation (replaces HeaderView). - * When [browseUrl] is non-null and non-blank the row is clickable and shows a chevron. + * Section header row used throughout the app. Title with optional subtitle, an optional + * trailing slot, and the whole row becomes clickable when [onClick] is non-null. When + * [trailing] is null and [onClick] is set, a default right-chevron is shown. */ @Composable fun SectionHeader( modifier: Modifier = Modifier, title: String, - browseUrl: String? = null, - onClick: () -> Unit = {} -) { - val hasLink = !browseUrl.isNullOrBlank() - Row( - modifier = modifier - .fillMaxWidth() - .then(if (hasLink) Modifier.clickable(onClick = onClick) else Modifier) - .padding(all = dimensionResource(R.dimen.padding_medium)), - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = title, - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f) - ) - if (hasLink) { - Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) - Icon( - painter = painterResource(R.drawable.ic_arrow_right), - contentDescription = null, - modifier = Modifier.size(dimensionResource(R.dimen.icon_size_default)), - tint = MaterialTheme.colorScheme.primary - ) - } - } -} - -/** - * Section header row with optional subtitle and right-aligned action button - * (replaces UpdateHeaderView). Pass `action = null` to omit the button entirely. - */ -@Composable -fun SectionHeaderWithAction( - modifier: Modifier = Modifier, - title: String, - action: String? = null, subtitle: String? = null, - onAction: () -> Unit = {} + onClick: (() -> Unit)? = null, + trailing: (@Composable () -> Unit)? = null ) { Row( modifier = modifier .fillMaxWidth() + .then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier) .padding( - start = dimensionResource(R.dimen.padding_small), - top = dimensionResource(R.dimen.padding_xxsmall), - bottom = dimensionResource(R.dimen.padding_xxsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), + horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { Column(modifier = Modifier.weight(1f)) { Text( text = title, style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.SemiBold, maxLines = 1, overflow = TextOverflow.Ellipsis ) @@ -108,10 +69,14 @@ fun SectionHeaderWithAction( ) } } - if (!action.isNullOrBlank()) { - TextButton(onClick = onAction) { - Text(action) - } + when { + trailing != null -> trailing() + onClick != null -> Icon( + painter = painterResource(R.drawable.ic_arrow_right), + contentDescription = null, + modifier = Modifier.size(dimensionResource(R.dimen.icon_size_default)), + tint = MaterialTheme.colorScheme.primary + ) } } } @@ -120,12 +85,22 @@ fun SectionHeaderWithAction( @Preview(showBackground = true) @Composable private fun SectionHeaderPreview() { - SectionHeader(title = "Top Charts", browseUrl = "https://example.com") + SectionHeader(title = "Top Charts", onClick = {}) +} + +@PreviewWrapper(ThemePreviewProvider::class) +@Preview(showBackground = true) +@Composable +private fun SectionHeaderWithSubtitlePreview() { + SectionHeader(title = "Permissions", subtitle = "3 requested", onClick = {}) } @PreviewWrapper(ThemePreviewProvider::class) @Preview(showBackground = true) @Composable private fun SectionHeaderWithActionPreview() { - SectionHeaderWithAction(title = "3 updates available", action = "Update all") + SectionHeader( + title = "3 updates available", + trailing = { TextButton(onClick = {}) { Text("Update all") } } + ) } diff --git a/app/src/main/java/com/aurora/store/compose/composable/Shimmer.kt b/app/src/main/java/com/aurora/store/compose/composable/Shimmer.kt index 5e663fa0f..e604b78f0 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/Shimmer.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/Shimmer.kt @@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -66,159 +65,129 @@ fun shimmerBrush(): Brush { } @Composable -internal fun ShimmerCategoryRow() { - val brush = shimmerBrush() +private fun ShimmerBlock(modifier: Modifier, radiusRes: Int = R.dimen.radius_small) { + Box( + modifier = modifier + .clip(RoundedCornerShape(dimensionResource(radiusRes))) + .background(shimmerBrush()) + ) +} + +@Composable +private fun ShimmerTextStack(modifier: Modifier = Modifier, widthFractions: List) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) + ) { + widthFractions.forEach { fraction -> + ShimmerBlock( + modifier = Modifier + .fillMaxWidth(fraction) + .height(14.dp) + ) + } + } +} + +@Composable +private fun ShimmerListRow(showTrailing: Boolean) { Row( modifier = Modifier .fillMaxWidth() .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_small), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { - Box( - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_category)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) + ShimmerBlock( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)) ) - - Box( - modifier = Modifier - .weight(1f) - .height(24.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) + ShimmerTextStack( + modifier = Modifier.weight(1f), + widthFractions = listOf(0.7f, 0.5f, 0.5f) ) + if (showTrailing) { + ShimmerBlock( + modifier = Modifier + .width(80.dp) + .height(36.dp), + radiusRes = R.dimen.radius_large + ) + } } } @Composable internal fun ShimmerAppRow() { - val brush = shimmerBrush() - Row( - modifier = Modifier - .fillMaxWidth() - .padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_xsmall) - ), - verticalAlignment = Alignment.CenterVertically - ) { - Box( - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_medium)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) - ) - Column( - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)) - ) { - repeat(3) { i -> - Box( - modifier = Modifier - .fillMaxWidth(if (i == 0) 0.75f else 0.5f) - .height(14.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) - ) - if (i < 2) Spacer(Modifier.height(dimensionResource(R.dimen.padding_xxsmall))) - } - } - } + ShimmerListRow(showTrailing = false) } @Composable internal fun ShimmerUpdateItem() { - val brush = shimmerBrush() + ShimmerListRow(showTrailing = true) +} + +@Composable +internal fun ShimmerCategoryRow() { Row( modifier = Modifier .fillMaxWidth() .padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_xsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large)) ) { - Box( - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_medium)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) + ShimmerBlock( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_category)) ) - - Column(modifier = Modifier.weight(1f)) { - repeat(3) { i -> - Box( - modifier = Modifier - .fillMaxWidth(if (i == 0) 0.7f else 0.5f) - .height(14.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) - ) - if (i < 2) Spacer(Modifier.height(dimensionResource(R.dimen.padding_xxsmall))) - } - } - - Box( + ShimmerBlock( modifier = Modifier - .width(80.dp) - .height(36.dp) - .clip(RoundedCornerShape(50)) - .background(brush) + .weight(1f) + .height(24.dp) ) } } @Composable internal fun ShimmerCarouselSection() { - val brush = shimmerBrush() Column( modifier = Modifier .fillMaxWidth() - .padding(all = dimensionResource(R.dimen.padding_small)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) + .padding(all = dimensionResource(R.dimen.spacing_small)), + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { - Box( + ShimmerBlock( modifier = Modifier .fillMaxWidth(0.42f) .height(16.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) ) - Row( modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { repeat(5) { Column( verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.padding_xsmall) + dimensionResource(R.dimen.spacing_xsmall) ) ) { - Box( - modifier = Modifier - .size(dimensionResource(R.dimen.icon_size_cluster)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) - .background(brush) + ShimmerBlock( + modifier = Modifier.size(dimensionResource(R.dimen.icon_size_cluster)), + radiusRes = R.dimen.radius_medium ) - Box( + ShimmerBlock( modifier = Modifier .width(dimensionResource(R.dimen.icon_size_cluster) * 0.75f) .height(11.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) ) - Box( + ShimmerBlock( modifier = Modifier .width(dimensionResource(R.dimen.icon_size_cluster) * 0.5f) .height(11.dp) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) - .background(brush) ) } } @@ -228,13 +197,11 @@ internal fun ShimmerCarouselSection() { @Composable internal fun ShimmerAppListItem() { - val brush = shimmerBrush() - Box( + ShimmerBlock( modifier = Modifier - .padding(dimensionResource(R.dimen.padding_xsmall)) - .size(dimensionResource(R.dimen.icon_size_cluster)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) - .background(brush) + .padding(dimensionResource(R.dimen.spacing_xsmall)) + .size(dimensionResource(R.dimen.icon_size_cluster)), + radiusRes = R.dimen.radius_medium ) } diff --git a/app/src/main/java/com/aurora/store/compose/composable/StreamCarousel.kt b/app/src/main/java/com/aurora/store/compose/composable/StreamCarousel.kt index 1c810db64..e77618b29 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/StreamCarousel.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/StreamCarousel.kt @@ -21,6 +21,8 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.gplayapi.data.models.App @@ -60,7 +62,7 @@ fun StreamCarousel( LazyColumn( modifier = modifier.fillMaxSize(), state = lazyListState, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { items(5) { ShimmerCarouselSection() } } @@ -75,10 +77,10 @@ fun StreamCarousel( } if (clusters.isEmpty()) { - EmptyState( + Placeholder( modifier = modifier, - icon = R.drawable.ic_apps, - message = R.string.no_apps_available + painter = painterResource(R.drawable.ic_apps), + message = stringResource(R.string.no_apps_available) ) return } @@ -89,9 +91,9 @@ fun StreamCarousel( verticalArrangement = Arrangement.spacedBy( dimensionResource( if (clusters.size == 1) { - R.dimen.margin_medium + R.dimen.spacing_medium } else { - R.dimen.margin_xxsmall + R.dimen.spacing_xsmall } ) ) @@ -109,8 +111,11 @@ fun StreamCarousel( item(key = "header_${cluster.id}") { SectionHeader( title = cluster.clusterTitle, - browseUrl = cluster.clusterBrowseUrl, - onClick = { onHeaderClick(cluster) } + onClick = if (cluster.clusterBrowseUrl.isNotBlank()) { + { onHeaderClick(cluster) } + } else { + null + } ) } item(key = "row_${cluster.id}") { @@ -150,8 +155,8 @@ internal fun ClusterRow( LazyRow( state = rowState, - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_small)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_small)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { itemsIndexed( items = cluster.clusterAppList, diff --git a/app/src/main/java/com/aurora/store/compose/composable/TextDividerComposable.kt b/app/src/main/java/com/aurora/store/compose/composable/TextDividerComposable.kt index 320a9df8a..aa261b800 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/TextDividerComposable.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/TextDividerComposable.kt @@ -30,7 +30,7 @@ fun TextDividerComposable(modifier: Modifier = Modifier, title: String) { Text( modifier = modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_small)), + .padding(dimensionResource(R.dimen.spacing_small)), text = title.uppercase(), style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.primary, diff --git a/app/src/main/java/com/aurora/store/compose/composable/app/AppListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/app/AppListItem.kt index 3184b98fc..2d1e75c64 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/app/AppListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/app/AppListItem.kt @@ -48,10 +48,10 @@ fun AppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = { modifier = modifier .width(dimensionResource(R.dimen.icon_size_cluster)) .clickable(onClick = onClick) - .padding(all = dimensionResource(R.dimen.padding_xxsmall)), + .padding(all = dimensionResource(R.dimen.spacing_xsmall)), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_xsmall), + dimensionResource(R.dimen.spacing_xsmall), Alignment.CenterVertically ) ) { diff --git a/app/src/main/java/com/aurora/store/compose/composable/app/AppUpdateItem.kt b/app/src/main/java/com/aurora/store/compose/composable/app/AppUpdateItem.kt index 71de19aba..74cd57141 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/app/AppUpdateItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/app/AppUpdateItem.kt @@ -55,8 +55,8 @@ fun AppUpdateItem( .fillMaxWidth() .clickable(onClick = onClick) .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ), verticalAlignment = Alignment.CenterVertically ) { @@ -68,7 +68,7 @@ fun AppUpdateItem( progress = progress ) } - Spacer(Modifier.width(dimensionResource(R.dimen.margin_medium))) + Spacer(Modifier.width(dimensionResource(R.dimen.spacing_medium))) Column(modifier = Modifier.weight(1f)) { Text( text = update.displayName, @@ -91,7 +91,7 @@ fun AppUpdateItem( overflow = TextOverflow.Ellipsis ) } - Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) + Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small))) when { onUnignore != null -> { OutlinedButton(onClick = onUnignore) { diff --git a/app/src/main/java/com/aurora/store/compose/composable/app/LargeAppListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/app/LargeAppListItem.kt index bc78fdc63..1498fb652 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/app/LargeAppListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/app/LargeAppListItem.kt @@ -5,15 +5,9 @@ package com.aurora.store.compose.composable.app -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -21,7 +15,6 @@ import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -31,82 +24,49 @@ import coil3.request.crossfade import com.aurora.extensions.requiresGMS import com.aurora.gplayapi.data.models.App import com.aurora.store.R +import com.aurora.store.compose.composable.AuroraListItem import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.util.CommonUtil -/** - * Composable for displaying minimal app details in a vertical-scrollable list - * @param modifier The modifier to be applied to the composable - * @param app [App] to display - * @param onClick Callback when the composable is clicked - * @see AppListItem - */ @Composable fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {}) { - @Composable - fun buildExtras(app: App): List = mutableListOf().apply { - add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString) - add("${app.labeledRating}★") - - if (app.isFree) { - add(stringResource(R.string.details_free)) - } else { - add(stringResource(R.string.details_paid)) - } - - if (app.containsAds) { - add(stringResource(R.string.details_contains_ads)) - } else { - add(stringResource(R.string.details_no_ads)) - } - - if (app.requiresGMS()) { - add(stringResource(R.string.details_gsf_dependent)) - } - } - - Row( - modifier = modifier - .fillMaxWidth() - .clickable(onClick = onClick) - .padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_xsmall) - ) - ) { - AsyncImage( - modifier = Modifier - .requiredSize(dimensionResource(R.dimen.icon_size_medium)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))), - model = ImageRequest.Builder(LocalContext.current) - .data(app.iconArtwork.url) - .crossfade(true) - .build(), - contentDescription = null, - contentScale = ContentScale.Crop - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.margin_small)) - ) { - Text( - text = app.displayName, - style = MaterialTheme.typography.bodyMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text(text = app.developerName, style = MaterialTheme.typography.bodySmall) - Text( - text = buildExtras(app).joinToString(separator = " • "), - style = MaterialTheme.typography.bodySmall + AuroraListItem( + modifier = modifier, + headline = app.displayName, + supporting = app.developerName, + tertiary = buildAppExtras(app), + headlineStyle = MaterialTheme.typography.bodyMedium, + onClick = onClick, + leading = { + AsyncImage( + modifier = Modifier + .requiredSize(dimensionResource(R.dimen.icon_size_medium)) + .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))), + model = ImageRequest.Builder(LocalContext.current) + .data(app.iconArtwork.url) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop ) } - } + ) } +@Composable +private fun buildAppExtras(app: App): String = buildList { + add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString) + add("${app.labeledRating}★") + add(stringResource(if (app.isFree) R.string.details_free else R.string.details_paid)) + add( + stringResource( + if (app.containsAds) R.string.details_contains_ads else R.string.details_no_ads + ) + ) + if (app.requiresGMS()) add(stringResource(R.string.details_gsf_dependent)) +}.joinToString(separator = " • ") + @PreviewWrapper(ThemePreviewProvider::class) @Preview(showBackground = true) @Composable diff --git a/app/src/main/java/com/aurora/store/compose/composable/details/ExodusListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/details/ExodusListItem.kt index 69576ef9d..e49016dfe 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/details/ExodusListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/details/ExodusListItem.kt @@ -38,7 +38,7 @@ fun ExodusListItem(modifier: Modifier = Modifier, tracker: ExodusTracker) { onClick = { context.browse(tracker.url) }, enabled = tracker.url.isNotBlank() ) - .padding(dimensionResource(R.dimen.padding_small)) + .padding(dimensionResource(R.dimen.spacing_small)) ) { Text( text = tracker.name, diff --git a/app/src/main/java/com/aurora/store/compose/composable/details/RatingListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/details/RatingListItem.kt index 3a34b889f..202cb5adc 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/details/RatingListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/details/RatingListItem.kt @@ -34,9 +34,9 @@ fun RatingListItem(modifier: Modifier = Modifier, label: String, rating: Float) Row( modifier = modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.padding_small)), + .padding(horizontal = dimensionResource(R.dimen.spacing_small)), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { Text( text = label, diff --git a/app/src/main/java/com/aurora/store/compose/composable/details/ReviewListItem.kt b/app/src/main/java/com/aurora/store/compose/composable/details/ReviewListItem.kt index 9d6579c07..a1ffd0f7b 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/details/ReviewListItem.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/details/ReviewListItem.kt @@ -45,8 +45,8 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) { modifier = modifier .fillMaxWidth() .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ) ) { AsyncImage( @@ -61,7 +61,7 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) { .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) ) Column( - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)) + modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_small)) ) { Text( text = review.userName, diff --git a/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt index e5cc278a3..255d4cfbf 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/about/AboutScreen.kt @@ -101,7 +101,7 @@ private fun ScreenContent(onAboutAurora: () -> Unit = {}) { modifier = Modifier .fillMaxSize() .padding(paddingValues), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { @@ -128,9 +128,9 @@ private fun BrandHeader() { Column( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.margin_medium)), + .padding(dimensionResource(R.dimen.spacing_medium)), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) diff --git a/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt index 1f0c7de50..bbd9a6af6 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/accounts/AccountsScreen.kt @@ -113,7 +113,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_small)), + .padding(vertical = dimensionResource(R.dimen.spacing_small)), verticalArrangement = Arrangement.SpaceBetween ) { AssistHeader() @@ -121,7 +121,7 @@ private fun ScreenContent( Column( modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) @@ -153,7 +153,9 @@ private fun ScreenContent( .fillMaxWidth() .padding(bottom = dimensionResource(R.dimen.height_bottom_adj)), horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_medium) + ) ) { Text( text = stringResource(R.string.account_logout), @@ -166,7 +168,7 @@ private fun ScreenContent( painter = painterResource(R.drawable.ic_logout), contentDescription = null ) - Spacer(modifier = Modifier.width(dimensionResource(R.dimen.padding_xsmall))) + Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing_xsmall))) Text( text = stringResource(R.string.action_logout) ) @@ -188,8 +190,8 @@ private fun AssistHeader() { LazyRow( modifier = Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { items(items = links.keys.toList(), key = { item -> item }) { label -> AssistChip( diff --git a/app/src/main/java/com/aurora/store/compose/ui/apps/TopChartsPage.kt b/app/src/main/java/com/aurora/store/compose/ui/apps/TopChartsPage.kt index 347b03fc0..95d23d4a1 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/apps/TopChartsPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/apps/TopChartsPage.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper @@ -37,7 +38,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.helpers.contracts.TopChartsContract import com.aurora.store.R -import com.aurora.store.compose.composable.EmptyState +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ShimmerAppRow import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composition.collectForced @@ -121,7 +122,7 @@ private fun TopChartsBody( ) { SecondaryScrollableTabRow( selectedTabIndex = selectedIndex, - edgePadding = dimensionResource(R.dimen.padding_small) + edgePadding = dimensionResource(R.dimen.spacing_small) ) { chartTitles.forEachIndexed { index, titleRes -> Tab( @@ -133,18 +134,18 @@ private fun TopChartsBody( } when { - state is ViewState.Error -> EmptyState( + state is ViewState.Error -> Placeholder( modifier = Modifier.weight(1f), - icon = R.drawable.ic_apps, - message = R.string.no_apps_available, - actionLabel = R.string.action_retry, + painter = painterResource(R.drawable.ic_apps), + message = stringResource(R.string.no_apps_available), + actionLabel = stringResource(R.string.action_retry), onAction = onRetry ) - cluster != null && cluster.clusterAppList.isEmpty() -> EmptyState( + cluster != null && cluster.clusterAppList.isEmpty() -> Placeholder( modifier = Modifier.weight(1f), - icon = R.drawable.ic_apps, - message = R.string.no_apps_available + painter = painterResource(R.drawable.ic_apps), + message = stringResource(R.string.no_apps_available) ) cluster != null -> { @@ -155,10 +156,10 @@ private fun TopChartsBody( .fillMaxWidth(), state = listState, contentPadding = PaddingValues( - vertical = dimensionResource(R.dimen.padding_small) + vertical = dimensionResource(R.dimen.spacing_small) ), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_xsmall) + dimensionResource(R.dimen.spacing_xsmall) ) ) { items(count = apps.size, key = { apps[it].id }) { index -> @@ -172,7 +173,7 @@ private fun TopChartsBody( Box( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_large)), + .padding(dimensionResource(R.dimen.spacing_large)), contentAlignment = Alignment.Center ) { CircularProgressIndicator() @@ -186,8 +187,10 @@ private fun TopChartsBody( modifier = Modifier .weight(1f) .fillMaxWidth(), - contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.padding_small)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xsmall)) + contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.spacing_small)), + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_xsmall) + ) ) { items(8) { ShimmerAppRow() } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt index fd71e7272..ada1710ea 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/blacklist/BlacklistScreen.kt @@ -248,8 +248,8 @@ private fun ScreenContent( .fillMaxWidth() .clickable { onRequestSearch(pkg.displayName) } .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ) ) } @@ -298,7 +298,7 @@ private fun ScreenContent( modifier = Modifier.fillMaxWidth(), state = listState, verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_xxsmall) + dimensionResource(R.dimen.spacing_xsmall) ) ) { if (selectedPackages.isNotEmpty()) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt index 036b19556..93a1c7aa9 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt @@ -15,7 +15,7 @@ import androidx.compose.ui.res.stringResource import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.store.R -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.StreamCarousel import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composition.collectForced @@ -40,7 +40,7 @@ fun CategoryBrowseScreen( topBar = { TopAppBar(title = title) } ) { paddingValues -> if (uiState is ViewState.Error) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt index cd696d810..4d9736c0d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt @@ -22,7 +22,7 @@ import androidx.paging.LoadState import androidx.paging.compose.collectAsLazyPagingItems import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.navigation.Destination @@ -54,7 +54,7 @@ fun ExpandedStreamBrowseScreen( is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Error -> { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) @@ -63,7 +63,7 @@ fun ExpandedStreamBrowseScreen( else -> { if (apps.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.no_apps_available) @@ -74,7 +74,7 @@ fun ExpandedStreamBrowseScreen( .fillMaxSize() .padding(paddingValues), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items( diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/MoreSheet.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/MoreSheet.kt index e3a320588..416911a27 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/MoreSheet.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/MoreSheet.kt @@ -93,8 +93,8 @@ fun MoreSheet( Column( Modifier.padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_small), + vertical = dimensionResource(R.dimen.spacing_small) ) ) { mainItems.forEach { item -> @@ -119,8 +119,8 @@ fun MoreSheet( Column( Modifier.padding( - horizontal = dimensionResource(R.dimen.padding_small), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_small), + vertical = dimensionResource(R.dimen.spacing_small) ) ) { extraItems.forEach { item -> @@ -153,7 +153,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () -> Row( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_large)), + .padding(dimensionResource(R.dimen.spacing_large)), verticalAlignment = Alignment.CenterVertically ) { AsyncImage( @@ -171,7 +171,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () -> Column( modifier = Modifier .weight(1f) - .padding(horizontal = dimensionResource(R.dimen.padding_normal)) + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)) ) { Text( text = if (isAnonymous) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/NetworkSheet.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/NetworkSheet.kt index 5df746c89..2631eb993 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/NetworkSheet.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/NetworkSheet.kt @@ -49,8 +49,8 @@ fun NetworkSheet() { Column( modifier = Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.padding_xlarge)) - .padding(bottom = dimensionResource(R.dimen.padding_large)), + .padding(horizontal = dimensionResource(R.dimen.spacing_xlarge)) + .padding(bottom = dimensionResource(R.dimen.spacing_large)), horizontalAlignment = Alignment.CenterHorizontally ) { Icon( @@ -58,17 +58,17 @@ fun NetworkSheet() { contentDescription = null, modifier = Modifier.requiredSize(48.dp) ) - Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_large))) + Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_large))) Text( text = stringResource(R.string.title_no_network), style = MaterialTheme.typography.titleLarge ) - Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_small))) + Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_small))) Text( text = stringResource(R.string.check_connectivity), style = MaterialTheme.typography.bodyMedium ) - Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_xlarge))) + Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_xlarge))) Button( modifier = Modifier.fillMaxWidth(), onClick = { diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt index a3571e00c..577990f2a 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt @@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.navigation.Destination @@ -73,7 +73,7 @@ private fun ScreenContent( is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Error -> { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) @@ -82,7 +82,7 @@ private fun ScreenContent( else -> { if (apps.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.no_apps_available) @@ -93,7 +93,7 @@ private fun ScreenContent( .fillMaxSize() .padding(paddingValues), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items( diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index 2e16e1931..7d7bc77d5 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -66,8 +66,7 @@ import com.aurora.store.ComposeActivity import com.aurora.store.R import com.aurora.store.compose.composable.ClusterRow import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error -import com.aurora.store.compose.composable.Header +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.ShimmerCarouselSection @@ -212,7 +211,7 @@ private fun ScreenContentError(message: String? = null) { Scaffold( topBar = { TopAppBar() } ) { paddingValues -> - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_apps_outage), message = message ?: stringResource(R.string.toast_app_unavailable) @@ -426,7 +425,7 @@ private fun ScreenContentApp( modifier = Modifier .fillMaxSize(), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ), state = listState ) { @@ -451,7 +450,7 @@ private fun ScreenContentApp( } item { - Header( + SectionHeader( title = stringResource(R.string.details_more_about_app), subtitle = app.shortDescription, onClick = { showExtraPane(ExtraScreen.More) } @@ -487,7 +486,7 @@ private fun ScreenContentApp( } item { - Header( + SectionHeader( title = stringResource(R.string.details_permission), subtitle = if (app.permissions.isNotEmpty()) { stringResource(R.string.permissions_requested, app.permissions.size) diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt index d6602fbce..68f78cbac 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ExodusScreen.kt @@ -40,9 +40,9 @@ import com.aurora.extensions.browse import com.aurora.extensions.isWindowCompact import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.compose.composable.Error -import com.aurora.store.compose.composable.Header +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.details.ExodusListItem import com.aurora.store.compose.preview.AppPreviewProvider @@ -128,12 +128,12 @@ private fun ScreenContentReport( LazyColumn( modifier = Modifier .fillMaxSize() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), state = listState ) { stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { - Header( + SectionHeader( title = if (report?.trackers.isNullOrEmpty()) { stringResource(R.string.exodus_no_tracker) } else { @@ -178,11 +178,11 @@ private fun ScreenContentError( ) } ) { paddingValues -> - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.failed_to_fetch_report), - actionMessage = stringResource(R.string.action_request_analysis), + actionLabel = stringResource(R.string.action_request_analysis), onAction = onRequestAnalysis ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt index 4ebd59b64..314ee138f 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ManualDownloadScreen.kt @@ -139,12 +139,14 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(dimensionResource(R.dimen.padding_medium)), + .padding(dimensionResource(R.dimen.spacing_medium)), verticalArrangement = Arrangement.SpaceBetween ) { Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_medium) + ) ) { Info( painter = painterResource(R.drawable.ic_download_manager), @@ -181,7 +183,7 @@ private fun ScreenContent( Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.padding_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { FilledTonalButton( diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt index bb5c7010e..82f17f2fb 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt @@ -103,10 +103,10 @@ private fun ScreenContent( modifier = Modifier .fillMaxWidth() .navigationBarsPadding() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)) - .padding(vertical = dimensionResource(R.dimen.padding_small)), + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_small)), horizontalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.padding_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { FilledTonalButton( @@ -147,7 +147,7 @@ private fun ScreenContent( LazyColumn( modifier = Modifier .fillMaxSize() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), state = listState ) { item { diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt index 5212c40db..3995c106d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/MoreScreen.kt @@ -38,9 +38,9 @@ import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.isWindowCompact import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info import com.aurora.store.compose.composable.ScrollHint +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.app.AppListItem import com.aurora.store.compose.navigation.Destination @@ -100,16 +100,18 @@ private fun ScreenContent( LazyColumn( modifier = Modifier.fillMaxSize(), state = listState, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_medium) + ) ) { item { - Header(title = stringResource(R.string.details_description)) + SectionHeader(title = stringResource(R.string.details_description)) } item { Text( modifier = Modifier.padding( - horizontal = dimensionResource(R.dimen.padding_medium) + horizontal = dimensionResource(R.dimen.spacing_medium) ), text = AnnotatedString.fromHtml( htmlString = app.description @@ -144,7 +146,7 @@ private fun ScreenContent( */ @Composable private fun AppDependencies(dependencies: List, onNavigateTo: (Destination) -> Unit) { - Header(title = stringResource(R.string.details_dependencies)) + SectionHeader(title = stringResource(R.string.details_dependencies)) if (dependencies.isEmpty()) { Info( title = AnnotatedString(text = stringResource(R.string.details_no_dependencies)) @@ -166,7 +168,7 @@ private fun AppDependencies(dependencies: List, onNavigateTo: (Destination) */ @Composable private fun AppInfoMore(app: App) { - Header(title = stringResource(R.string.details_more_info)) + SectionHeader(title = stringResource(R.string.details_more_info)) Info( title = AnnotatedString( text = stringResource(R.string.details_more_package_name) diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt index 1fc1e911d..e0893795a 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/PermissionScreen.kt @@ -94,9 +94,11 @@ private fun ScreenContent( LazyColumn( modifier = Modifier .fillMaxSize() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), state = listState, - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_medium) + ) ) { items(items = permissionsInfo.keys.toList(), key = { it }) { permission -> val permissionInfo = permissionsInfo.getValue(permission) diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt index f0fd4b088..f7bf883b7 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt @@ -49,7 +49,7 @@ import com.aurora.extensions.isWindowCompact import com.aurora.gplayapi.data.models.Review import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.details.ReviewListItem @@ -114,7 +114,7 @@ private fun ScreenContent( is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Error -> { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) @@ -167,8 +167,8 @@ private fun FilterHeader(onClick: (filter: Review.Filter) -> Unit) { LazyRow( modifier = Modifier.fillMaxWidth(), - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.margin_normal)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { items(items = filters.keys.toList(), key = { item -> item }) { filter -> val selected = activeFilter == filter diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Actions.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Actions.kt index d74fbbe83..002d245ba 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Actions.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Actions.kt @@ -53,8 +53,8 @@ fun Actions( Row( modifier = Modifier .fillMaxWidth() - .padding(PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium))), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) + .padding(PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium))), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { val buttonWidthModifier = when { windowAdaptiveInfo.isWindowCompact -> Modifier.weight(1F) @@ -92,7 +92,7 @@ fun Actions( @Composable private fun ActionsPreview() { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Actions( primaryActionDisplayName = stringResource(R.string.action_install), diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Changelog.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Changelog.kt index f3775c0b9..cb2d495fc 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Changelog.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Changelog.kt @@ -27,7 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.compose.composable.Header +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider @@ -38,11 +38,11 @@ import com.aurora.store.compose.preview.ThemePreviewProvider */ @Composable fun Changelog(changelog: String) { - Header(title = stringResource(R.string.details_changelog)) + SectionHeader(title = stringResource(R.string.details_changelog)) Box( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_medium)) + .padding(dimensionResource(R.dimen.spacing_medium)) ) { Box( modifier = Modifier @@ -50,8 +50,8 @@ fun Changelog(changelog: String) { .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) .background(color = MaterialTheme.colorScheme.secondaryContainer) .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ) ) { Text( @@ -71,7 +71,7 @@ fun Changelog(changelog: String) { @Composable private fun ChangelogPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Changelog(changelog = app.changes) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Compatibility.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Compatibility.kt index 310d4cf91..0e036f80b 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Compatibility.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Compatibility.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.theme.successColor import com.aurora.store.compose.theme.warningColor @@ -30,7 +30,7 @@ import com.aurora.store.data.model.Scores */ @Composable fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) { - Header( + SectionHeader( title = stringResource(R.string.details_compatibility_title), subtitle = stringResource(R.string.plexus_powered) ) @@ -82,7 +82,7 @@ fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) { @Composable private fun CompatibilityPreview() { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Compatibility(needsGms = true) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/DataSafety.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/DataSafety.kt index 94b03fe5a..7f986bc6b 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/DataSafety.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/DataSafety.kt @@ -22,8 +22,8 @@ import com.aurora.gplayapi.data.models.datasafety.Entry import com.aurora.gplayapi.data.models.datasafety.EntryType import com.aurora.gplayapi.data.models.datasafety.Report import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider @@ -37,7 +37,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider fun DataSafety(report: Report, privacyPolicyUrl: String) { val context = LocalContext.current - Header( + SectionHeader( title = stringResource(R.string.details_data_safety_title), subtitle = stringResource(R.string.details_data_safety_subtitle), onClick = { context.browse(privacyPolicyUrl) } @@ -86,7 +86,7 @@ fun DataSafety(report: Report, privacyPolicyUrl: String) { @Composable private fun DataSafetyPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { DataSafety( privacyPolicyUrl = app.privacyPolicyUrl, diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Details.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Details.kt index ad26a55c8..71b6c88d7 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Details.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Details.kt @@ -109,7 +109,7 @@ fun Details( Row( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.margin_medium)) + .padding(dimensionResource(R.dimen.spacing_medium)) ) { AnimatedAppIcon( modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), @@ -117,7 +117,7 @@ fun Details( inProgress = state.inProgress(), progress = state.progress() ) - Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))) { + Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_small))) { Text( text = app.displayName, style = MaterialTheme.typography.titleLarge, @@ -172,7 +172,7 @@ fun Details( @Composable private fun DetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Details(app = app) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/DeveloperDetails.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/DeveloperDetails.kt index fd89cba51..7b7ffe0cf 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/DeveloperDetails.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/DeveloperDetails.kt @@ -22,8 +22,8 @@ import com.aurora.extensions.copyToClipBoard import com.aurora.extensions.mailTo import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider @@ -38,7 +38,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider fun DeveloperDetails(address: String, website: String, email: String) { val context = LocalContext.current - Header(title = stringResource(R.string.details_dev_details)) + SectionHeader(title = stringResource(R.string.details_dev_details)) Column { if (website.isNotBlank()) { Info( @@ -74,7 +74,7 @@ fun DeveloperDetails(address: String, website: String, email: String) { @Composable private fun DeveloperDetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { DeveloperDetails( address = app.developerAddress, diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Privacy.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Privacy.kt index 9a0b2fd58..0fdaed4b5 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Privacy.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Privacy.kt @@ -15,8 +15,8 @@ import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.theme.successColor import com.aurora.store.compose.theme.warningColor @@ -30,7 +30,7 @@ import com.aurora.store.data.model.Report */ @Composable fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) { - Header( + SectionHeader( title = stringResource(R.string.details_privacy), subtitle = stringResource(R.string.exodus_powered), onClick = onNavigateToDetailsExodus @@ -64,7 +64,7 @@ fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) { @Composable private fun PrivacyPreview() { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Privacy(report = Report(), onNavigateToDetailsExodus = {}) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/RatingAndReviews.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/RatingAndReviews.kt index f4e1b379e..cb2663d27 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/RatingAndReviews.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/RatingAndReviews.kt @@ -40,7 +40,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Rating import com.aurora.gplayapi.data.models.Review import com.aurora.store.R -import com.aurora.store.compose.composable.Header +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.details.RatingListItem import com.aurora.store.compose.composable.details.ReviewListItem import com.aurora.store.compose.preview.AppPreviewProvider @@ -82,7 +82,7 @@ fun RatingAndReviews( } } - Header( + SectionHeader( title = stringResource(R.string.details_ratings), onClick = onNavigateToDetailsReview ) @@ -90,12 +90,12 @@ fun RatingAndReviews( Row( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_medium)), + .padding(dimensionResource(R.dimen.spacing_medium)), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_large)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large)) ) { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)), + modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { @@ -114,7 +114,7 @@ fun RatingAndReviews( } Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { stars.reversed().fastForEach { star -> RatingListItem( @@ -129,8 +129,8 @@ fun RatingAndReviews( val pagerState = rememberPagerState { featuredReviews.size } HorizontalPager( state = pagerState, - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), - pageSpacing = dimensionResource(R.dimen.margin_medium) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)), + pageSpacing = dimensionResource(R.dimen.spacing_medium) ) { page -> Box( modifier = Modifier @@ -158,7 +158,7 @@ private fun RatingAndReviewsPreview(@PreviewParameter(AppPreviewProvider::class) ) } Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { RatingAndReviews(rating = app.rating, featuredReviews = reviews) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Screenshots.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Screenshots.kt index a325d69a0..3cfe4884f 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Screenshots.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Screenshots.kt @@ -37,8 +37,8 @@ import com.aurora.store.compose.preview.ThemePreviewProvider @Composable fun Screenshots(screenshots: List, onNavigateToScreenshot: (index: Int) -> Unit = {}) { LazyRow( - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { items(items = screenshots, key = { artwork -> artwork.url }) { artwork -> ScreenshotListItem( @@ -57,7 +57,7 @@ fun Screenshots(screenshots: List, onNavigateToScreenshot: (index: Int) @Composable private fun ScreenshotsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Screenshots(screenshots = app.screenshots) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Tags.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Tags.kt index 12ffef31d..3c489e345 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Tags.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Tags.kt @@ -63,8 +63,8 @@ fun Tags(app: App) { ).filterKeys { it != null } LazyRow( - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { items(items = tags.keys.toList()) { label -> TagListItem(label = label!!, painter = painterResource(tags.getValue(label))) @@ -77,7 +77,7 @@ fun Tags(app: App) { @Composable private fun TagsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Tags(app = app) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Testing.kt b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Testing.kt index 58d3bc4d1..8a5d54c98 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/composable/Testing.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/composable/Testing.kt @@ -26,8 +26,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.gplayapi.data.models.App import com.aurora.store.R -import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Info +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider @@ -40,13 +40,13 @@ import com.aurora.store.compose.preview.ThemePreviewProvider */ @Composable fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) { - Header(title = stringResource(R.string.details_beta)) + SectionHeader(title = stringResource(R.string.details_beta)) Row( modifier = Modifier .fillMaxWidth() - .padding(dimensionResource(R.dimen.padding_medium)), + .padding(dimensionResource(R.dimen.spacing_medium)), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)) ) { Info( modifier = Modifier.weight(1F), @@ -79,7 +79,7 @@ fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Bool @Composable private fun TestingPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { Testing(isSubscribed = app.testingProgram!!.isSubscribed) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt index aa2b3ac2d..ef5e0fb5b 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -32,7 +32,7 @@ import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.navigation.Destination @@ -96,7 +96,7 @@ private fun ScreenContent( is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Error -> { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) @@ -108,9 +108,9 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)), + .padding(vertical = dimensionResource(R.dimen.spacing_medium)), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items(count = apps.itemCount, key = apps.itemKey { it.id }) { index -> diff --git a/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt index e5121bc7c..0896c6b5d 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dispenser/DispenserScreen.kt @@ -29,7 +29,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aurora.extensions.copyToClipBoard import com.aurora.store.R import com.aurora.store.compose.composable.DispenserListItem -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.viewmodel.dispenser.DispenserViewModel @@ -94,11 +94,11 @@ private fun ScreenContent( } ) { paddingValues -> if (dispensers.isEmpty()) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_server), message = stringResource(R.string.no_dispensers_available), - actionMessage = stringResource(R.string.add_dispenser_title), + actionLabel = stringResource(R.string.add_dispenser_title), onAction = onAddDispenser ) } else { @@ -106,7 +106,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_medium)) ) { items(items = dispensers.toList(), key = { url -> url }) { url -> DispenserListItem( diff --git a/app/src/main/java/com/aurora/store/compose/ui/dispenser/InputDispenserDialog.kt b/app/src/main/java/com/aurora/store/compose/ui/dispenser/InputDispenserDialog.kt index abfe565b9..c7db04883 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dispenser/InputDispenserDialog.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dispenser/InputDispenserDialog.kt @@ -53,7 +53,9 @@ fun InputDispenserDialog(onAdd: (url: String) -> Unit = {}, onDismiss: () -> Uni title = { Text(text = stringResource(R.string.add_dispenser_title)) }, text = { Column( - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + verticalArrangement = Arrangement.spacedBy( + dimensionResource(R.dimen.spacing_medium) + ) ) { Text(text = stringResource(R.string.add_dispenser_summary)) OutlinedTextField( diff --git a/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt index 500059878..38ababa77 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/downloads/DownloadsScreen.kt @@ -41,7 +41,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.DownloadListItem -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.navigation.Destination @@ -49,6 +49,7 @@ import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu import com.aurora.store.compose.ui.downloads.menu.MenuItem +import com.aurora.store.compose.ui.sheets.DownloadActionsSheet import com.aurora.store.data.model.DownloadStatus import com.aurora.store.data.room.download.Download import com.aurora.store.viewmodel.downloads.DownloadsViewModel @@ -114,6 +115,19 @@ private fun ScreenContent( * Save the initial loading state to make sure we don't replay the loading animation again. */ var initialLoad by rememberSaveable { mutableStateOf(true) } + var actionsTarget by rememberSaveable { mutableStateOf(null) } + + actionsTarget?.let { target -> + DownloadActionsSheet( + download = target, + onDismiss = { actionsTarget = null }, + onShowDetails = { onNavigateTo(Destination.AppDetails(target.packageName)) }, + onCancel = { onCancel(target.packageName) }, + onInstall = { onInstall(target) }, + onExport = { onExport(target) }, + onClear = { onClear(target) } + ) + } @Composable fun SetupMenu() { @@ -138,7 +152,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_medium)) ) { when { downloads.loadState.refresh is LoadState.Loading && initialLoad -> { @@ -149,7 +163,7 @@ private fun ScreenContent( initialLoad = false if (downloads.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_download_manager), message = stringResource(R.string.download_none) @@ -168,15 +182,7 @@ private fun ScreenContent( DownloadListItem( modifier = Modifier.animateItem(), download = download, - onClick = { - onNavigateTo( - Destination.AppDetails(download.packageName) - ) - }, - onClear = { onClear(download) }, - onCancel = { onCancel(download.packageName) }, - onExport = { onExport(download) }, - onInstall = { onInstall(download) } + onClick = { actionsTarget = download } ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt index 546693ed8..e59ab371c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/favourite/FavouriteScreen.kt @@ -40,8 +40,8 @@ import com.aurora.extensions.emptyPagingItems import com.aurora.extensions.toast import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.FavouriteListItem +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.navigation.Destination @@ -140,7 +140,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_medium)) ) { when { favourites.loadState.refresh is LoadState.Loading && initialLoad -> { @@ -151,7 +151,7 @@ private fun ScreenContent( initialLoad = false if (favourites.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_favorite_unchecked), message = stringResource(R.string.details_no_favourites) diff --git a/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt index ed7d9f6f3..6d26e4da1 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/installed/InstalledScreen.kt @@ -37,7 +37,7 @@ import com.aurora.extensions.emptyPagingItems import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.app.LargeAppListItem @@ -95,7 +95,7 @@ private fun ScreenContent( initialLoad = false if (apps.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_apps_outage), message = stringResource(R.string.no_apps_available) @@ -106,7 +106,7 @@ private fun ScreenContent( LazyColumn( state = listState, verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items( diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt index 9b763682b..29fd8177e 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt @@ -61,12 +61,12 @@ private fun ScreenContent( LazyColumn( modifier = Modifier .fillMaxSize() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), state = listState ) { item { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) + modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)) ) { Text( text = stringResource(R.string.onboarding_title_gsf), diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt index 2dc220178..53cf10dec 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt @@ -99,16 +99,16 @@ private fun ScreenContent( .fillMaxWidth() .navigationBarsPadding() .padding( - vertical = dimensionResource(R.dimen.padding_medium), + vertical = dimensionResource(R.dimen.spacing_medium), horizontal = if (windowAdaptiveInfo.isWindowCompact) { - dimensionResource(R.dimen.padding_medium) + dimensionResource(R.dimen.spacing_medium) } else { - dimensionResource(R.dimen.padding_xlarge) + dimensionResource(R.dimen.spacing_xlarge) } ), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.padding_medium), + dimensionResource(R.dimen.spacing_medium), Alignment.End ) ) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt index 96386d853..21201774c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt @@ -45,12 +45,12 @@ private fun PageContent( onPermissionCallback: (type: PermissionType) -> Unit = {} ) { PermissionList( - modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.padding_medium)), + modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_medium)), permissions = permissions, onPermissionCallback = onPermissionCallback, header = { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) + modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)) ) { Text( text = stringResource(R.string.onboarding_title_permissions), diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt index de260c6da..0fa9d5fe6 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt @@ -96,12 +96,12 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) { LazyColumn( modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)), + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { stickyHeader { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) + modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)) ) { Text( text = stringResource(R.string.onboarding_title_welcome), diff --git a/app/src/main/java/com/aurora/store/compose/ui/preferences/installation/InstallerScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/preferences/installation/InstallerScreen.kt index ef0cb9b62..09a9a367b 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/preferences/installation/InstallerScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/preferences/installation/InstallerScreen.kt @@ -76,7 +76,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_medium)) ) { items(items = availableInstallers, key = { i -> i.id }) { installerInfo -> InstallerListItem( diff --git a/app/src/main/java/com/aurora/store/compose/ui/preferences/network/NetworkPreferenceScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/preferences/network/NetworkPreferenceScreen.kt index cd0f0653d..9d5d4bfc1 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/preferences/network/NetworkPreferenceScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/preferences/network/NetworkPreferenceScreen.kt @@ -228,7 +228,7 @@ private fun ProxyURLDialog( Column { Text( text = stringResource(R.string.pref_network_proxy_url_message), - modifier = Modifier.padding(bottom = dimensionResource(R.dimen.padding_small)) + modifier = Modifier.padding(bottom = dimensionResource(R.dimen.spacing_small)) ) OutlinedTextField( value = url, @@ -255,7 +255,7 @@ private fun ProxyURLDialog( Text(stringResource(R.string.disable)) } } - Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) + Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small))) TextButton(onClick = onDismiss) { Text(stringResource(android.R.string.cancel)) } @@ -286,14 +286,14 @@ internal fun SingleChoiceDialog( onClick = { onSelect(index) }, role = Role.RadioButton ) - .padding(vertical = dimensionResource(R.dimen.padding_small)), + .padding(vertical = dimensionResource(R.dimen.spacing_small)), verticalAlignment = Alignment.CenterVertically ) { RadioButton( selected = index == selected, onClick = null ) - Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) + Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small))) Text(option) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/preferences/updates/UpdatesPreferenceScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/preferences/updates/UpdatesPreferenceScreen.kt index f457b5cf0..9ff5b97cb 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/preferences/updates/UpdatesPreferenceScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/preferences/updates/UpdatesPreferenceScreen.kt @@ -366,7 +366,7 @@ private fun UpdatesRestrictionsDialog(onUpdateAutomatedCheck: () -> Unit, onDism text = { Column { Text(stringResource(R.string.pref_updates_restrictions_desc)) - Spacer(Modifier.height(dimensionResource(R.dimen.margin_small))) + Spacer(Modifier.height(dimensionResource(R.dimen.spacing_small))) Row( modifier = Modifier .fillMaxWidth() diff --git a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt index 1a443f1ad..9f8302ef5 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt @@ -74,7 +74,7 @@ import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App import com.aurora.store.R import com.aurora.store.compose.composable.ContainedLoadingIndicator -import com.aurora.store.compose.composable.Error +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.SearchSuggestionListItem import com.aurora.store.compose.composable.app.LargeAppListItem @@ -238,7 +238,7 @@ private fun ScreenContent( modifier = Modifier .padding(paddingValues) .fillMaxSize() - .padding(vertical = dimensionResource(R.dimen.padding_medium)) + .padding(vertical = dimensionResource(R.dimen.spacing_medium)) ) { FilterHeader( isEnabled = isSearching && results.loadState.refresh is LoadState.NotLoading, @@ -250,7 +250,7 @@ private fun ScreenContent( is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Error -> { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.error) @@ -259,7 +259,7 @@ private fun ScreenContent( else -> { if (isSearching && results.itemCount == 0) { - Error( + Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), message = stringResource(R.string.no_apps_available) @@ -271,7 +271,7 @@ private fun ScreenContent( state = listState, modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items( @@ -316,7 +316,7 @@ private fun ScreenContent( else -> { if (isSearching && results.itemCount > 0) { - Error( + Placeholder( painter = painterResource(R.drawable.ic_round_search), message = stringResource(R.string.select_app_for_details) ) @@ -435,8 +435,8 @@ private fun FilterHeader( LazyRow( modifier = Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.padding_medium)), - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) + .padding(horizontal = dimensionResource(R.dimen.spacing_medium)), + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { items(items = filters, key = { item -> item }) { filter -> val isSelected = when (filter) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/sheets/AppUpdateSheet.kt b/app/src/main/java/com/aurora/store/compose/ui/sheets/AppUpdateSheet.kt index db12afc2e..7c4642a4c 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/sheets/AppUpdateSheet.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/sheets/AppUpdateSheet.kt @@ -82,7 +82,7 @@ fun AppUpdateSheet( HorizontalDivider( modifier = Modifier.padding( - vertical = dimensionResource(R.dimen.padding_small) + vertical = dimensionResource(R.dimen.spacing_small) ) ) @@ -107,7 +107,7 @@ fun AppUpdateSheet( HorizontalDivider( modifier = Modifier.padding( - vertical = dimensionResource(R.dimen.padding_small) + vertical = dimensionResource(R.dimen.spacing_small) ) ) @@ -159,11 +159,11 @@ private fun AppHeader(update: Update, onShowDetails: () -> Unit) { modifier = Modifier .fillMaxWidth() .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) ) { AsyncImage( model = ImageRequest.Builder(LocalContext.current) @@ -212,12 +212,12 @@ private fun Item(label: String, onClick: () -> Unit) { style = MaterialTheme.typography.bodyLarge, modifier = Modifier .fillMaxWidth() - .padding(horizontal = dimensionResource(R.dimen.padding_xsmall)) - .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_normal))) + .padding(horizontal = dimensionResource(R.dimen.spacing_xsmall)) + .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) .clickable(onClick = onClick) .padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_small) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) ) ) } @@ -228,8 +228,8 @@ private fun ChangelogSection(html: String) { text = stringResource(R.string.details_changelog), style = MaterialTheme.typography.titleSmall, modifier = Modifier.padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) ) ) @@ -238,8 +238,8 @@ private fun ChangelogSection(html: String) { style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding( - horizontal = dimensionResource(R.dimen.padding_medium), - vertical = dimensionResource(R.dimen.padding_xsmall) + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) ) ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/sheets/DownloadActionsSheet.kt b/app/src/main/java/com/aurora/store/compose/ui/sheets/DownloadActionsSheet.kt new file mode 100644 index 000000000..086bba308 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/sheets/DownloadActionsSheet.kt @@ -0,0 +1,181 @@ +/* + * SPDX-FileCopyrightText: 2026 Aurora OSS + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +package com.aurora.store.compose.ui.sheets + +import android.text.format.Formatter +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredSize +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.FilledTonalButton +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ModalBottomSheet +import androidx.compose.material3.Text +import androidx.compose.material3.rememberModalBottomSheetState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextOverflow +import com.aurora.store.R +import com.aurora.store.compose.composable.app.AnimatedAppIcon +import com.aurora.store.data.room.download.Download +import com.aurora.store.util.PackageUtil + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun DownloadActionsSheet( + download: Download, + onDismiss: () -> Unit, + onShowDetails: () -> Unit, + onCancel: () -> Unit, + onInstall: () -> Unit, + onExport: () -> Unit, + onClear: () -> Unit +) { + val context = LocalContext.current + val canInstall = download.canInstall(context) + val canExport = canInstall || PackageUtil.isInstalled(context, download.packageName) + + ModalBottomSheet( + onDismissRequest = onDismiss, + sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(rememberScrollState()) + ) { + DownloadHeader( + download = download, + onShowDetails = { + onShowDetails() + onDismiss() + } + ) + + HorizontalDivider( + modifier = Modifier.padding(vertical = dimensionResource(R.dimen.spacing_xsmall)) + ) + + Item( + label = stringResource(R.string.action_cancel), + enabled = download.isRunning, + onClick = { + onCancel() + onDismiss() + } + ) + Item( + label = stringResource(R.string.action_install), + enabled = canInstall, + onClick = { + onInstall() + onDismiss() + } + ) + Item( + label = stringResource(R.string.action_clear), + enabled = !download.isRunning, + onClick = { + onClear() + onDismiss() + } + ) + Item( + label = stringResource(R.string.action_export), + enabled = canExport, + onClick = { + onExport() + onDismiss() + } + ) + + Spacer(Modifier.navigationBarsPadding()) + } + } +} + +@Composable +private fun DownloadHeader(download: Download, onShowDetails: () -> Unit) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding( + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_xsmall) + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium)) + ) { + AnimatedAppIcon( + modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)), + iconUrl = download.iconURL, + inProgress = download.isRunning, + progress = download.progress.toFloat() + ) + Column(modifier = Modifier.weight(1f)) { + Text( + text = download.displayName, + style = MaterialTheme.typography.titleSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = download.packageName, + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + text = Formatter.formatShortFileSize(LocalContext.current, download.size), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + FilledTonalButton(onClick = onShowDetails) { + Text(stringResource(R.string.updates_app_details)) + } + } +} + +@Composable +private fun Item(label: String, enabled: Boolean = true, onClick: () -> Unit) { + Text( + text = label, + style = MaterialTheme.typography.bodyLarge, + color = if (enabled) { + MaterialTheme.colorScheme.onSurface + } else { + MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f) + }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = dimensionResource(R.dimen.spacing_xsmall)) + .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) + .clickable(enabled = enabled, onClick = onClick) + .padding( + horizontal = dimensionResource(R.dimen.spacing_medium), + vertical = dimensionResource(R.dimen.spacing_small) + ) + ) +} diff --git a/app/src/main/java/com/aurora/store/compose/ui/splash/SplashScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/splash/SplashScreen.kt index 06cec48fd..cab620f00 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/splash/SplashScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/splash/SplashScreen.kt @@ -227,7 +227,7 @@ fun SplashScreen( modifier = Modifier .fillMaxWidth() .weight(0.1f) - .padding(horizontal = dimensionResource(R.dimen.padding_xlarge)), + .padding(horizontal = dimensionResource(R.dimen.spacing_xlarge)), contentAlignment = Alignment.Center ) { val (statusText, statusColor) = when { @@ -260,7 +260,7 @@ fun SplashScreen( Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_small) + dimensionResource(R.dimen.spacing_small) ) ) { Button( diff --git a/app/src/main/java/com/aurora/store/compose/ui/spoof/DevicePage.kt b/app/src/main/java/com/aurora/store/compose/ui/spoof/DevicePage.kt index 118920676..fc6c4a8b3 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/spoof/DevicePage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/spoof/DevicePage.kt @@ -53,7 +53,7 @@ private fun PageContent( ) { LazyColumn( modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/spoof/LocalePage.kt b/app/src/main/java/com/aurora/store/compose/ui/spoof/LocalePage.kt index 83d8b0ece..9130e91d3 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/spoof/LocalePage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/spoof/LocalePage.kt @@ -52,7 +52,7 @@ private fun PageContent( ) { LazyColumn( modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) + verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall)) ) { stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { diff --git a/app/src/main/java/com/aurora/store/compose/ui/updates/UpdatesScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/updates/UpdatesScreen.kt index 41f3931cc..b691aee53 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/updates/UpdatesScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/updates/UpdatesScreen.kt @@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -17,12 +19,13 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.aurora.store.R -import com.aurora.store.compose.composable.EmptyState -import com.aurora.store.compose.composable.SectionHeaderWithAction +import com.aurora.store.compose.composable.Placeholder +import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.ShimmerUpdateItem import com.aurora.store.compose.composable.app.AppUpdateItem import com.aurora.store.compose.navigation.Destination @@ -82,7 +85,7 @@ fun UpdatesScreen( LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { items(10) { ShimmerUpdateItem() } @@ -90,11 +93,11 @@ fun UpdatesScreen( } updateMap.isEmpty() -> { - EmptyState( + Placeholder( modifier = Modifier.fillMaxSize(), - icon = R.drawable.ic_updates, - message = R.string.details_no_updates, - actionLabel = R.string.check_updates, + painter = painterResource(R.drawable.ic_updates), + message = stringResource(R.string.details_no_updates), + actionLabel = stringResource(R.string.check_updates), onAction = { viewModel.fetchUpdates() } ) } @@ -103,7 +106,7 @@ fun UpdatesScreen( LazyColumn( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy( - dimensionResource(R.dimen.margin_medium) + dimensionResource(R.dimen.spacing_medium) ) ) { if (mainEntries.isNotEmpty()) { @@ -122,15 +125,18 @@ fun UpdatesScreen( R.string.action_update_all } ) - SectionHeaderWithAction( + SectionHeader( title = title, - action = actionLabel, - onAction = { - if (mainAllEnqueued) { - onCancelAll() - } else { - onRequestUpdateAll(mainEntries.map { it.key }) - } + trailing = { + TextButton( + onClick = { + if (mainAllEnqueued) { + onCancelAll() + } else { + onRequestUpdateAll(mainEntries.map { it.key }) + } + } + ) { Text(actionLabel) } } ) } @@ -145,12 +151,15 @@ fun UpdatesScreen( if (approvalEntries.isNotEmpty()) { item(key = "header_approval") { - SectionHeaderWithAction( + SectionHeader( title = stringResource(R.string.updates_approval_header), subtitle = stringResource(R.string.updates_approval_desc), - action = stringResource(R.string.action_update_all), - onAction = { - onRequestUpdateAll(approvalEntries.map { it.key }) + trailing = { + TextButton( + onClick = { + onRequestUpdateAll(approvalEntries.map { it.key }) + } + ) { Text(stringResource(R.string.action_update_all)) } } ) } @@ -165,7 +174,7 @@ fun UpdatesScreen( if (incompatibleEntries.isNotEmpty()) { item(key = "header_incompatible") { - SectionHeaderWithAction( + SectionHeader( title = stringResource(R.string.updates_incompatible_header), subtitle = stringResource(R.string.updates_incompatible_desc) ) @@ -181,7 +190,7 @@ fun UpdatesScreen( if (ignoredUpdates.isNotEmpty()) { item(key = "header_ignored") { - SectionHeaderWithAction( + SectionHeader( title = stringResource(R.string.updates_ignored_header), subtitle = stringResource(R.string.updates_ignored_desc) ) diff --git a/app/src/main/res/drawable/bg_changelog.xml b/app/src/main/res/drawable/bg_changelog.xml index 6c4160581..d9a5ea2f4 100644 --- a/app/src/main/res/drawable/bg_changelog.xml +++ b/app/src/main/res/drawable/bg_changelog.xml @@ -22,9 +22,9 @@ + android:bottom="@dimen/spacing_medium" + android:left="@dimen/spacing_medium" + android:right="@dimen/spacing_medium" + android:top="@dimen/spacing_medium" /> diff --git a/app/src/main/res/drawable/divider.xml b/app/src/main/res/drawable/divider.xml index decd864d1..6c82c7e7e 100644 --- a/app/src/main/res/drawable/divider.xml +++ b/app/src/main/res/drawable/divider.xml @@ -21,6 +21,6 @@ android:shape="rectangle"> + android:width="@dimen/spacing_small" + android:height="@dimen/spacing_small" /> diff --git a/app/src/main/res/drawable/tab_indicator.xml b/app/src/main/res/drawable/tab_indicator.xml index 9b7a64f9c..939a8193e 100644 --- a/app/src/main/res/drawable/tab_indicator.xml +++ b/app/src/main/res/drawable/tab_indicator.xml @@ -23,7 +23,7 @@ + android:right="@dimen/spacing_xlarge" /> diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index a29bb956f..2cfe2648b 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -18,21 +18,11 @@ --> - 2dp - 4dp - 8dp - 10dp - 12dp - 16dp - 32dp - - 2dp - 4dp - 8dp - 10dp - 12dp - 16dp - 32dp + 4dp + 8dp + 12dp + 16dp + 24dp 24dp 36dp @@ -69,8 +59,7 @@ 200dp 8dp - 10dp - 12dp + 12dp 16dp 5dp