compose: housekeeping

This commit is contained in:
Rahul Patel
2026-05-19 16:40:37 +05:30
parent c4bc76e2a4
commit 5c6bcd6361
83 changed files with 940 additions and 1316 deletions

View File

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

View File

@@ -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() })
}
)
}
@PreviewWrapper(ThemePreviewProvider::class)

View File

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

View File

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

View File

@@ -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() })
}
)
}
@Preview(showBackground = true)

View File

@@ -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,29 +23,14 @@ 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),
@@ -69,6 +39,7 @@ fun DispenserListItem(
)
}
}
)
}
@Preview(showBackground = true)

View File

@@ -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)
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()
}
}
@Composable
fun ExpandedMenu() {
fun DownloadListItem(modifier: Modifier = Modifier, download: Download, onClick: () -> Unit = {}) {
val context = LocalContext.current
Box(modifier = modifier) {
IconButton(onClick = { isExpanded = true }) {
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
)
}
}
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)
}
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)) {
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()
)
Column(
modifier = Modifier
.padding(horizontal = dimensionResource(R.dimen.margin_small))
) {
Text(
text = download.displayName,
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
},
trailing = if (download.isFinished) {
{
Icon(
painter = painterResource(R.drawable.ic_check),
contentDescription = stringResource(R.string.download_completed),
tint = Color(0xFF4CAF50)
)
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()
null
}
)
}
}
}
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))
}

View File

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

View File

@@ -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)
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
),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(modifier = Modifier.weight(1F)) {
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
)
}
}
IconButton(onClick = { requestClear() }) {
},
trailing = {
IconButton(onClick = triggerRemove) {
Icon(
painter = painterResource(R.drawable.ic_favorite_checked),
contentDescription = stringResource(R.string.action_favourite)
)
}
}
)
}
}

View File

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

View File

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

View File

@@ -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
AuroraListItem(
modifier = modifier,
headline = stringResource(installerInfo.title),
supporting = stringResource(installerInfo.subtitle),
tertiary = stringResource(installerInfo.description),
onClick = onClick,
trailing = { RadioButton(selected = isSelected, onClick = onClick) }
)
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)
}
}
@PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -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,14 +32,12 @@ 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))
) {
AuroraListItem(
modifier = modifier,
headline = link.title,
supporting = link.subtitle,
onClick = onClick,
leading = {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(link.icon)
@@ -70,27 +49,8 @@ fun LinkListItem(
.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
)
}
}
)
}
@PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -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() })
}
)
}
@Preview(showBackground = true)

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,37 +18,17 @@ 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) {
@@ -69,6 +41,7 @@ fun PermissionListItem(
)
}
}
)
}
@PreviewWrapper(ThemePreviewProvider::class)

View File

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

View File

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

View File

@@ -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,13 +51,8 @@ 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),
@@ -95,6 +60,7 @@ fun SearchSuggestionListItem(
)
}
}
)
}
@PreviewWrapper(ThemePreviewProvider::class)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,50 +24,21 @@ 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<String> = mutableListOf<String>().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)
)
) {
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))
@@ -86,26 +50,22 @@ fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Uni
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
@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
)
Text(text = app.developerName, style = MaterialTheme.typography.bodySmall)
Text(
text = buildExtras(app).joinToString(separator = ""),
style = MaterialTheme.typography.bodySmall
)
}
}
}
if (app.requiresGMS()) add(stringResource(R.string.details_gsf_dependent))
}.joinToString(separator = "")
@PreviewWrapper(ThemePreviewProvider::class)
@Preview(showBackground = true)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,8 +37,8 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
@Composable
fun Screenshots(screenshots: List<Artwork>, 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<Artwork>, 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)
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,16 +125,19 @@ fun UpdatesScreen(
R.string.action_update_all
}
)
SectionHeaderWithAction(
SectionHeader(
title = title,
action = actionLabel,
onAction = {
trailing = {
TextButton(
onClick = {
if (mainAllEnqueued) {
onCancelAll()
} else {
onRequestUpdateAll(mainEntries.map { it.key })
}
}
) { Text(actionLabel) }
}
)
}
updateItems(
@@ -145,13 +151,16 @@ 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 = {
trailing = {
TextButton(
onClick = {
onRequestUpdateAll(approvalEntries.map { it.key })
}
) { Text(stringResource(R.string.action_update_all)) }
}
)
}
updateItems(
@@ -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)
)

View File

@@ -22,9 +22,9 @@
<corners android:radius="8dp" />
<solid android:color="?colorControlHighlight" />
<padding
android:bottom="@dimen/margin_medium"
android:left="@dimen/margin_medium"
android:right="@dimen/margin_medium"
android:top="@dimen/margin_medium" />
android:bottom="@dimen/spacing_medium"
android:left="@dimen/spacing_medium"
android:right="@dimen/spacing_medium"
android:top="@dimen/spacing_medium" />
</shape>

View File

@@ -21,6 +21,6 @@
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<size
android:width="@dimen/margin_small"
android:height="@dimen/margin_small" />
android:width="@dimen/spacing_small"
android:height="@dimen/spacing_small" />
</shape>

View File

@@ -23,7 +23,7 @@
<solid android:color="?colorAccent" />
<padding
android:left="0dp"
android:right="@dimen/padding_xlarge" />
android:right="@dimen/spacing_xlarge" />
<corners android:radius="5dp" />
</shape>
</item>

View File

@@ -18,21 +18,11 @@
-->
<resources>
<dimen name="padding_xxsmall">2dp</dimen>
<dimen name="padding_xsmall">4dp</dimen>
<dimen name="padding_small">8dp</dimen>
<dimen name="padding_medium">10dp</dimen>
<dimen name="padding_normal">12dp</dimen>
<dimen name="padding_large">16dp</dimen>
<dimen name="padding_xlarge">32dp</dimen>
<dimen name="margin_xxsmall">2dp</dimen>
<dimen name="margin_xsmall">4dp</dimen>
<dimen name="margin_small">8dp</dimen>
<dimen name="margin_medium">10dp</dimen>
<dimen name="margin_normal">12dp</dimen>
<dimen name="margin_large">16dp</dimen>
<dimen name="margin_xlarge">32dp</dimen>
<dimen name="spacing_xsmall">4dp</dimen>
<dimen name="spacing_small">8dp</dimen>
<dimen name="spacing_medium">12dp</dimen>
<dimen name="spacing_large">16dp</dimen>
<dimen name="spacing_xlarge">24dp</dimen>
<dimen name="icon_size_default">24dp</dimen>
<dimen name="icon_size_category">36dp</dimen>
@@ -69,8 +59,7 @@
<dimen name="review_height">200dp</dimen>
<dimen name="radius_small">8dp</dimen>
<dimen name="radius_medium">10dp</dimen>
<dimen name="radius_normal">12dp</dimen>
<dimen name="radius_medium">12dp</dimen>
<dimen name="radius_large">16dp</dimen>
<dimen name="scroll_hint_padding">5dp</dimen>