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.Bitmap
import android.graphics.Color import android.graphics.Color
import androidx.compose.foundation.Image 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.foundation.layout.requiredSize
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
@@ -33,18 +24,6 @@ import com.aurora.store.BuildConfig
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider 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 @Composable
fun BlackListItem( fun BlackListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -57,48 +36,25 @@ fun BlackListItem(
isEnabled: Boolean = true, isEnabled: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = displayName,
.clickable(enabled = isEnabled, onClick = onClick) supporting = packageName,
.padding( tertiary = stringResource(R.string.version, versionName, versionCode),
horizontal = dimensionResource(R.dimen.padding_medium), headlineStyle = MaterialTheme.typography.bodyMedium,
vertical = dimensionResource(R.dimen.padding_xsmall) onClick = onClick,
), enabled = isEnabled,
horizontalArrangement = Arrangement.SpaceBetween, leading = {
verticalAlignment = Alignment.CenterVertically
) {
Row(modifier = Modifier.weight(1F)) {
Image( Image(
bitmap = icon.asImageBitmap(), bitmap = icon.asImageBitmap(),
contentDescription = null, contentDescription = null,
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)) modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium))
) )
Column( },
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)) trailing = {
) {
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
)
}
}
Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() }) Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() })
} }
)
} }
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -38,11 +38,11 @@ fun CategoryItem(modifier: Modifier = Modifier, category: Category, onClick: ()
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
) { ) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)

View File

@@ -31,7 +31,7 @@ fun ContainedLoadingIndicator(modifier: Modifier = Modifier) {
Box( Box(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.padding(dimensionResource(R.dimen.padding_small)), .padding(dimensionResource(R.dimen.spacing_small)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
val description = stringResource(R.string.loading) val description = stringResource(R.string.loading)

View File

@@ -5,34 +5,13 @@
package com.aurora.store.compose.composable 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.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource 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.Preview
import com.aurora.store.R 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 @Composable
fun DeviceListItem( fun DeviceListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -43,34 +22,16 @@ fun DeviceListItem(
isChecked: Boolean = false, isChecked: Boolean = false,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = userReadableName,
.clickable(onClick = { if (!isChecked) onClick() }) supporting = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk),
.padding(dimensionResource(R.dimen.padding_small)), tertiary = platforms.replace(",\\s*".toRegex(), ", "),
verticalAlignment = Alignment.CenterVertically, onClick = { if (!isChecked) onClick() },
horizontalArrangement = Arrangement.SpaceBetween trailing = {
) {
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
)
}
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
} }
)
} }
@Preview(showBackground = true) @Preview(showBackground = true)

View File

@@ -5,32 +5,17 @@
package com.aurora.store.compose.composable 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.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import com.aurora.store.R 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 @Composable
fun DispenserListItem( fun DispenserListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -38,29 +23,14 @@ fun DispenserListItem(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
onClear: () -> Unit = {} onClear: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = url,
.clickable(onClick = onClick) onClick = onClick,
.padding(dimensionResource(R.dimen.padding_small)), leading = {
horizontalArrangement = Arrangement.SpaceBetween, Icon(painter = painterResource(R.drawable.ic_server), contentDescription = null)
verticalAlignment = Alignment.CenterVertically },
) { trailing = {
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
)
}
TextButton(onClick = onClear) { TextButton(onClick = onClear) {
Text( Text(
text = stringResource(R.string.remove), text = stringResource(R.string.remove),
@@ -69,6 +39,7 @@ fun DispenserListItem(
) )
} }
} }
)
} }
@Preview(showBackground = true) @Preview(showBackground = true)

View File

@@ -8,36 +8,16 @@ package com.aurora.store.compose.composable
import android.text.format.DateUtils import android.text.format.DateUtils
import android.text.format.Formatter 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.foundation.layout.requiredSize
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper 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.model.DownloadStatus
import com.aurora.store.data.room.download.Download import com.aurora.store.data.room.download.Download
import com.aurora.store.util.CommonUtil.getETAString 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 @Composable
fun ExpandedMenu() { fun DownloadListItem(modifier: Modifier = Modifier, download: Download, onClick: () -> Unit = {}) {
val context = LocalContext.current val context = LocalContext.current
Box(modifier = modifier) {
IconButton(onClick = { isExpanded = true }) { val statusLine = stringResource(download.status.localized)
Icon( val tertiaryLine = if (download.status in DownloadStatus.running) {
painter = painterResource(R.drawable.ic_more_vert), val progress = "${download.progress}%"
contentDescription = stringResource(R.string.menu) val speed = "${Formatter.formatShortFileSize(context, download.speed)}/s"
) val eta = getETAString(context, download.timeRemaining)
} "$progress$speed$eta"
DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) { } else {
DropdownMenuItem( DateUtils.formatDateTime(context, download.downloadedAt, DateUtils.FORMAT_SHOW_DATE)
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
)
}
}
} }
AnimatedVisibility(visible = isVisible, exit = shrinkVertically() + fadeOut()) { AuroraListItem(
Row( modifier = modifier,
modifier = modifier headline = download.displayName,
.fillMaxWidth() supporting = statusLine,
.clickable(onClick = onClick) tertiary = tertiaryLine,
.padding( headlineStyle = MaterialTheme.typography.bodyMedium,
horizontal = dimensionResource(R.dimen.padding_medium), onClick = onClick,
vertical = dimensionResource(R.dimen.padding_small) leading = {
),
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(modifier = Modifier.weight(1F)) {
AnimatedAppIcon( AnimatedAppIcon(
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)), modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)),
iconUrl = download.iconURL, iconUrl = download.iconURL,
inProgress = download.isRunning, inProgress = download.isRunning,
progress = download.progress.toFloat() progress = download.progress.toFloat()
) )
Column( },
modifier = Modifier trailing = if (download.isFinished) {
.padding(horizontal = dimensionResource(R.dimen.margin_small)) {
) { Icon(
Text( painter = painterResource(R.drawable.ic_check),
text = download.displayName, contentDescription = stringResource(R.string.download_completed),
style = MaterialTheme.typography.bodyMedium, tint = Color(0xFF4CAF50)
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
Text( }
text = stringResource(download.status.localized),
style = MaterialTheme.typography.bodySmall
)
AnimatedContent(targetState = download.status) { status ->
Text(
style = MaterialTheme.typography.bodySmall,
text = if (status in DownloadStatus.running) {
"$progress$speed$eta"
} else { } else {
DateUtils.formatDateTime( null
LocalContext.current,
download.downloadedAt,
DateUtils.FORMAT_SHOW_DATE
).toString()
} }
) )
} }
}
}
ExpandedMenu()
}
}
}
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun DownloadListItemPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { 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 package com.aurora.store.compose.composable
import android.text.format.DateUtils 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.layout.requiredSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale 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.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper 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.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.room.favourite.Favourite 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 @Composable
fun FavouriteListItem( fun FavouriteListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -64,35 +38,22 @@ fun FavouriteListItem(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
onClear: () -> Unit = {} onClear: () -> Unit = {}
) { ) {
val coroutineScope = rememberCoroutineScope() val context = LocalContext.current
var isVisible by remember { mutableStateOf(true) } RemovableListItem(onRemove = onClear) { triggerRemove ->
AuroraListItem(
fun requestClear() { modifier = modifier,
coroutineScope.launch { headline = favourite.displayName,
isVisible = false supporting = favourite.packageName,
delay(300) // Let the animation play tertiary = DateUtils.formatDateTime(
onClear() context,
} favourite.added,
} 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_xsmall)
), ),
verticalAlignment = Alignment.CenterVertically, headlineStyle = MaterialTheme.typography.bodyMedium,
horizontalArrangement = Arrangement.SpaceBetween onClick = onClick,
) { leading = {
Row(modifier = Modifier.weight(1F)) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(context)
.data(favourite.iconURL) .data(favourite.iconURL)
.crossfade(true) .crossfade(true)
.build(), .build(),
@@ -102,40 +63,16 @@ fun FavouriteListItem(
.requiredSize(dimensionResource(R.dimen.icon_size_medium)) .requiredSize(dimensionResource(R.dimen.icon_size_medium))
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
) )
Column( },
modifier = Modifier.padding( trailing = {
horizontal = dimensionResource(R.dimen.margin_small) IconButton(onClick = triggerRemove) {
)
) {
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() }) {
Icon( Icon(
painter = painterResource(R.drawable.ic_favorite_checked), painter = painterResource(R.drawable.ic_favorite_checked),
contentDescription = stringResource(R.string.action_favourite) 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() .fillMaxWidth()
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_xxsmall) 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 verticalAlignment = Alignment.CenterVertically
) { ) {
if (painter != null) Icon(painter = painter, contentDescription = null) if (painter != null) Icon(painter = painter, contentDescription = null)

View File

@@ -5,35 +5,16 @@
package com.aurora.store.compose.composable 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.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.installer.SessionInstaller import com.aurora.store.data.installer.SessionInstaller
import com.aurora.store.data.model.InstallerInfo 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 @Composable
fun InstallerListItem( fun InstallerListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -41,34 +22,14 @@ fun InstallerListItem(
isSelected: Boolean = false, isSelected: Boolean = false,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = stringResource(installerInfo.title),
.clickable(onClick = onClick) supporting = stringResource(installerInfo.subtitle),
.padding(dimensionResource(R.dimen.padding_small)), tertiary = stringResource(installerInfo.description),
horizontalArrangement = Arrangement.SpaceBetween, onClick = onClick,
verticalAlignment = Alignment.CenterVertically trailing = { RadioButton(selected = isSelected, onClick = onClick) }
) {
Column(modifier = Modifier.weight(1F)) {
Text(
text = stringResource(installerInfo.title),
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
Text(
text = stringResource(installerInfo.subtitle),
style = MaterialTheme.typography.bodyMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(installerInfo.description),
style = MaterialTheme.typography.bodySmall
)
}
RadioButton(selected = isSelected, onClick = onClick)
}
} }
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -6,21 +6,9 @@
package com.aurora.store.compose.composable 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.requiredSize
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape 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.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color 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.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
@@ -38,12 +25,6 @@ import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.data.model.Link 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 @Composable
fun LinkListItem( fun LinkListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -51,14 +32,12 @@ fun LinkListItem(
onClick: () -> Unit = {}, onClick: () -> Unit = {},
iconTint: Color? = null iconTint: Color? = null
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = link.title,
.clickable(onClick = onClick) supporting = link.subtitle,
.padding(dimensionResource(R.dimen.padding_small)), onClick = onClick,
verticalAlignment = Alignment.CenterVertically, leading = {
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
.data(link.icon) .data(link.icon)
@@ -70,27 +49,8 @@ fun LinkListItem(
.clip(CircleShape), .clip(CircleShape),
colorFilter = if (iconTint != null) ColorFilter.tint(color = iconTint) else null 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) @PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -5,32 +5,12 @@
package com.aurora.store.compose.composable 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.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier 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 androidx.compose.ui.tooling.preview.Preview
import com.aurora.store.R
import java.util.Locale 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 @Composable
fun LocaleListItem( fun LocaleListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -39,30 +19,15 @@ fun LocaleListItem(
isChecked: Boolean = false, isChecked: Boolean = false,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = displayName,
.clickable(onClick = { if (!isChecked) onClick() }) supporting = displayLanguage,
.padding(dimensionResource(R.dimen.padding_small)), onClick = { if (!isChecked) onClick() },
verticalAlignment = Alignment.CenterVertically, trailing = {
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
)
}
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() }) Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
} }
)
} }
@Preview(showBackground = true) @Preview(showBackground = true)

View File

@@ -36,7 +36,7 @@ fun Logo(modifier: Modifier = Modifier) {
Column( Column(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_small), dimensionResource(R.dimen.spacing_small),
Alignment.CenterVertically Alignment.CenterVertically
), ),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally

View File

@@ -91,13 +91,13 @@ fun MicroG(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_small),
vertical = dimensionResource(R.dimen.padding_xxsmall) vertical = dimensionResource(R.dimen.spacing_xsmall)
), ),
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_gms_microg), text = stringResource(R.string.onboarding_gms_microg),
@@ -116,8 +116,8 @@ fun MicroG(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = dimensionResource(R.dimen.padding_large)), .padding(vertical = dimensionResource(R.dimen.spacing_large)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
uiState.items.fastForEach { item -> uiState.items.fastForEach { item ->
MicroGItemRow(item = item) MicroGItemRow(item = item)
@@ -127,7 +127,7 @@ fun MicroG(
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_small) dimensionResource(R.dimen.spacing_small)
) )
) { ) {
Checkbox( Checkbox(
@@ -218,8 +218,8 @@ private fun MicroGItemRow(item: ExternalItem) {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_small),
vertical = dimensionResource(R.dimen.padding_xsmall) vertical = dimensionResource(R.dimen.spacing_xsmall)
), ),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@@ -232,7 +232,7 @@ private fun MicroGItemRow(item: ExternalItem) {
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1F) .weight(1F)
.padding(horizontal = dimensionResource(R.dimen.margin_small)) .padding(horizontal = dimensionResource(R.dimen.spacing_small))
) { ) {
Text( Text(
text = item.displayName, text = item.displayName,

View File

@@ -43,7 +43,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I
modifier = modifier.fillMaxWidth(), modifier = modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_xsmall), dimensionResource(R.dimen.spacing_xsmall),
Alignment.CenterHorizontally Alignment.CenterHorizontally
) )
) { ) {
@@ -60,7 +60,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I
) )
val size by animateDpAsState( val size by animateDpAsState(
targetValue = if (isSelected) { targetValue = if (isSelected) {
dimensionResource(R.dimen.radius_normal) dimensionResource(R.dimen.radius_medium)
} else { } else {
dimensionResource(R.dimen.radius_small) dimensionResource(R.dimen.radius_small)
}, },

View File

@@ -133,7 +133,7 @@ fun PermissionList(
LazyColumn( LazyColumn(
modifier = modifier.fillMaxSize(), 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) if (header != null) stickyHeader(content = header)

View File

@@ -5,18 +5,10 @@
package com.aurora.store.compose.composable 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.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview 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.Permission
import com.aurora.store.data.model.PermissionType 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 @Composable
fun PermissionListItem( fun PermissionListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
permission: Permission, permission: Permission,
onAction: () -> Unit = {} onAction: () -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = permission.title,
.padding(dimensionResource(R.dimen.padding_small)), supporting = permission.subtitle,
verticalAlignment = Alignment.CenterVertically, trailing = {
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
)
}
TextButton(onClick = onAction, enabled = !permission.isGranted) { TextButton(onClick = onAction, enabled = !permission.isGranted) {
Text( Text(
text = if (permission.isGranted) { text = if (permission.isGranted) {
@@ -69,6 +41,7 @@ fun PermissionListItem(
) )
} }
} }
)
} }
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -1,4 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2026 Aurora OSS
* SPDX-FileCopyrightText: 2025 The Calyx Institute * SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later * 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.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
@@ -29,27 +30,23 @@ import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
/** /**
* Composable to show error message that fills all available screen * Full-screen empty/error placeholder: centered icon + message and an optional action button.
* @param modifier The modifier to be applied to the composable * Replaces the previous EmptyState and Error composables.
* @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
*/ */
@Composable @Composable
fun Error( fun Placeholder(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
painter: Painter, painter: Painter,
message: String, message: String,
actionMessage: String? = null, actionLabel: String? = null,
onAction: (() -> Unit)? = null onAction: (() -> Unit)? = null
) { ) {
Column( Column(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium)), .padding(dimensionResource(R.dimen.spacing_medium)),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_small), dimensionResource(R.dimen.spacing_small),
Alignment.CenterVertically Alignment.CenterVertically
), ),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
@@ -57,18 +54,18 @@ fun Error(
Icon( Icon(
painter = painter, painter = painter,
contentDescription = null, contentDescription = null,
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size)) modifier = Modifier.size(dimensionResource(R.dimen.icon_size_medium)),
tint = MaterialTheme.colorScheme.primary
) )
Text( Text(
modifier = Modifier.fillMaxWidth(),
text = message, text = message,
style = MaterialTheme.typography.bodyLarge,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
if (actionLabel != null && onAction != null) {
if (actionMessage != null) { FilledTonalButton(onClick = onAction) {
Button(onClick = { if (onAction != null) onAction() }, enabled = onAction != null) {
Text( Text(
text = actionMessage, text = actionLabel,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -80,10 +77,11 @@ fun Error(
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun ErrorPreview() { private fun PlaceholderPreview() {
Error( Placeholder(
painter = painterResource(R.drawable.ic_updates), painter = painterResource(R.drawable.ic_updates),
message = stringResource(R.string.details_no_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 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.foundation.layout.requiredSize
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
@@ -32,13 +23,6 @@ import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider 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 @Composable
fun SearchSuggestionListItem( fun SearchSuggestionListItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -46,25 +30,11 @@ fun SearchSuggestionListItem(
onClick: (query: String) -> Unit = {}, onClick: (query: String) -> Unit = {},
onAction: (query: String) -> Unit = {} onAction: (query: String) -> Unit = {}
) { ) {
Row( AuroraListItem(
modifier = modifier modifier = modifier,
.fillMaxWidth() headline = searchSuggestEntry.title,
.clickable(onClick = { onClick(searchSuggestEntry.title) }) onClick = { onClick(searchSuggestEntry.title) },
.padding( leading = {
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))
) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
.data( .data(
@@ -81,13 +51,8 @@ fun SearchSuggestionListItem(
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_default)) modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_default))
) )
Text( },
text = searchSuggestEntry.title, trailing = {
style = MaterialTheme.typography.bodyLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
IconButton(onClick = { onAction(searchSuggestEntry.title) }) { IconButton(onClick = { onAction(searchSuggestEntry.title) }) {
Icon( Icon(
painter = painterResource(R.drawable.ic_search_append), painter = painterResource(R.drawable.ic_search_append),
@@ -95,6 +60,7 @@ fun SearchSuggestionListItem(
) )
} }
} }
)
} }
@PreviewWrapper(ThemePreviewProvider::class) @PreviewWrapper(ThemePreviewProvider::class)

View File

@@ -1,18 +1,18 @@
/* /*
* SPDX-FileCopyrightText: 2026 Aurora OSS * SPDX-FileCopyrightText: 2026 Aurora OSS
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
*/ */
package com.aurora.store.compose.composable package com.aurora.store.compose.composable
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -22,7 +22,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
@@ -30,71 +29,33 @@ import com.aurora.store.R
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
/** /**
* Section header row with optional right-chevron for navigation (replaces HeaderView). * Section header row used throughout the app. Title with optional subtitle, an optional
* When [browseUrl] is non-null and non-blank the row is clickable and shows a chevron. * 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 @Composable
fun SectionHeader( fun SectionHeader(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
title: String, 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, subtitle: String? = null,
onAction: () -> Unit = {} onClick: (() -> Unit)? = null,
trailing: (@Composable () -> Unit)? = null
) { ) {
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier)
.padding( .padding(
start = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_medium),
top = dimensionResource(R.dimen.padding_xxsmall), vertical = dimensionResource(R.dimen.spacing_xsmall)
bottom = dimensionResource(R.dimen.padding_xxsmall)
), ),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = title, text = title,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold,
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -108,10 +69,14 @@ fun SectionHeaderWithAction(
) )
} }
} }
if (!action.isNullOrBlank()) { when {
TextButton(onClick = onAction) { trailing != null -> trailing()
Text(action) 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) @Preview(showBackground = true)
@Composable @Composable
private fun SectionHeaderPreview() { 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) @PreviewWrapper(ThemePreviewProvider::class)
@Preview(showBackground = true) @Preview(showBackground = true)
@Composable @Composable
private fun SectionHeaderWithActionPreview() { 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.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -66,159 +65,129 @@ fun shimmerBrush(): Brush {
} }
@Composable @Composable
internal fun ShimmerCategoryRow() { private fun ShimmerBlock(modifier: Modifier, radiusRes: Int = R.dimen.radius_small) {
val brush = shimmerBrush() 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( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_small),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_xsmall)
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium))
.requiredSize(dimensionResource(R.dimen.icon_size_category))
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
ShimmerTextStack(
Box( modifier = Modifier.weight(1f),
modifier = Modifier widthFractions = listOf(0.7f, 0.5f, 0.5f)
.weight(1f)
.height(24.dp)
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
if (showTrailing) {
ShimmerBlock(
modifier = Modifier
.width(80.dp)
.height(36.dp),
radiusRes = R.dimen.radius_large
)
}
} }
} }
@Composable @Composable
internal fun ShimmerAppRow() { internal fun ShimmerAppRow() {
val brush = shimmerBrush() ShimmerListRow(showTrailing = false)
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)))
}
}
}
} }
@Composable @Composable
internal fun ShimmerUpdateItem() { internal fun ShimmerUpdateItem() {
val brush = shimmerBrush() ShimmerListRow(showTrailing = true)
}
@Composable
internal fun ShimmerCategoryRow() {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_xsmall) vertical = dimensionResource(R.dimen.spacing_small)
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
) { ) {
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_category))
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
ShimmerBlock(
Column(modifier = Modifier.weight(1f)) {
repeat(3) { i ->
Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth(if (i == 0) 0.7f else 0.5f) .weight(1f)
.height(14.dp) .height(24.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)
) )
} }
} }
@Composable @Composable
internal fun ShimmerCarouselSection() { internal fun ShimmerCarouselSection() {
val brush = shimmerBrush()
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(all = dimensionResource(R.dimen.padding_small)), .padding(all = dimensionResource(R.dimen.spacing_small)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier
.fillMaxWidth(0.42f) .fillMaxWidth(0.42f)
.height(16.dp) .height(16.dp)
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
repeat(5) { repeat(5) {
Column( Column(
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_xsmall) dimensionResource(R.dimen.spacing_xsmall)
) )
) { ) {
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier.size(dimensionResource(R.dimen.icon_size_cluster)),
.size(dimensionResource(R.dimen.icon_size_cluster)) radiusRes = R.dimen.radius_medium
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
.background(brush)
) )
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier
.width(dimensionResource(R.dimen.icon_size_cluster) * 0.75f) .width(dimensionResource(R.dimen.icon_size_cluster) * 0.75f)
.height(11.dp) .height(11.dp)
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
Box( ShimmerBlock(
modifier = Modifier modifier = Modifier
.width(dimensionResource(R.dimen.icon_size_cluster) * 0.5f) .width(dimensionResource(R.dimen.icon_size_cluster) * 0.5f)
.height(11.dp) .height(11.dp)
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(brush)
) )
} }
} }
@@ -228,13 +197,11 @@ internal fun ShimmerCarouselSection() {
@Composable @Composable
internal fun ShimmerAppListItem() { internal fun ShimmerAppListItem() {
val brush = shimmerBrush() ShimmerBlock(
Box(
modifier = Modifier modifier = Modifier
.padding(dimensionResource(R.dimen.padding_xsmall)) .padding(dimensionResource(R.dimen.spacing_xsmall))
.size(dimensionResource(R.dimen.icon_size_cluster)) .size(dimensionResource(R.dimen.icon_size_cluster)),
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) radiusRes = R.dimen.radius_medium
.background(brush)
) )
} }

View File

@@ -21,6 +21,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
@@ -60,7 +62,7 @@ fun StreamCarousel(
LazyColumn( LazyColumn(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
state = lazyListState, state = lazyListState,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
items(5) { ShimmerCarouselSection() } items(5) { ShimmerCarouselSection() }
} }
@@ -75,10 +77,10 @@ fun StreamCarousel(
} }
if (clusters.isEmpty()) { if (clusters.isEmpty()) {
EmptyState( Placeholder(
modifier = modifier, modifier = modifier,
icon = R.drawable.ic_apps, painter = painterResource(R.drawable.ic_apps),
message = R.string.no_apps_available message = stringResource(R.string.no_apps_available)
) )
return return
} }
@@ -89,9 +91,9 @@ fun StreamCarousel(
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource( dimensionResource(
if (clusters.size == 1) { if (clusters.size == 1) {
R.dimen.margin_medium R.dimen.spacing_medium
} else { } else {
R.dimen.margin_xxsmall R.dimen.spacing_xsmall
} }
) )
) )
@@ -109,8 +111,11 @@ fun StreamCarousel(
item(key = "header_${cluster.id}") { item(key = "header_${cluster.id}") {
SectionHeader( SectionHeader(
title = cluster.clusterTitle, title = cluster.clusterTitle,
browseUrl = cluster.clusterBrowseUrl, onClick = if (cluster.clusterBrowseUrl.isNotBlank()) {
onClick = { onHeaderClick(cluster) } { onHeaderClick(cluster) }
} else {
null
}
) )
} }
item(key = "row_${cluster.id}") { item(key = "row_${cluster.id}") {
@@ -150,8 +155,8 @@ internal fun ClusterRow(
LazyRow( LazyRow(
state = rowState, state = rowState,
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_small)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_small)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
itemsIndexed( itemsIndexed(
items = cluster.clusterAppList, items = cluster.clusterAppList,

View File

@@ -30,7 +30,7 @@ fun TextDividerComposable(modifier: Modifier = Modifier, title: String) {
Text( Text(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_small)), .padding(dimensionResource(R.dimen.spacing_small)),
text = title.uppercase(), text = title.uppercase(),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,

View File

@@ -48,10 +48,10 @@ fun AppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {
modifier = modifier modifier = modifier
.width(dimensionResource(R.dimen.icon_size_cluster)) .width(dimensionResource(R.dimen.icon_size_cluster))
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding(all = dimensionResource(R.dimen.padding_xxsmall)), .padding(all = dimensionResource(R.dimen.spacing_xsmall)),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_xsmall), dimensionResource(R.dimen.spacing_xsmall),
Alignment.CenterVertically Alignment.CenterVertically
) )
) { ) {

View File

@@ -55,8 +55,8 @@ fun AppUpdateItem(
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
), ),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@@ -68,7 +68,7 @@ fun AppUpdateItem(
progress = progress progress = progress
) )
} }
Spacer(Modifier.width(dimensionResource(R.dimen.margin_medium))) Spacer(Modifier.width(dimensionResource(R.dimen.spacing_medium)))
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( Text(
text = update.displayName, text = update.displayName,
@@ -91,7 +91,7 @@ fun AppUpdateItem(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
when { when {
onUnignore != null -> { onUnignore != null -> {
OutlinedButton(onClick = onUnignore) { OutlinedButton(onClick = onUnignore) {

View File

@@ -5,15 +5,9 @@
package com.aurora.store.compose.composable.app 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.layout.requiredSize
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip 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.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource 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.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
@@ -31,50 +24,21 @@ import coil3.request.crossfade
import com.aurora.extensions.requiresGMS import com.aurora.extensions.requiresGMS
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.util.CommonUtil 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 @Composable
fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {}) { fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {}) {
@Composable AuroraListItem(
fun buildExtras(app: App): List<String> = mutableListOf<String>().apply { modifier = modifier,
add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString) headline = app.displayName,
add("${app.labeledRating}") supporting = app.developerName,
tertiary = buildAppExtras(app),
if (app.isFree) { headlineStyle = MaterialTheme.typography.bodyMedium,
add(stringResource(R.string.details_free)) onClick = onClick,
} else { leading = {
add(stringResource(R.string.details_paid))
}
if (app.containsAds) {
add(stringResource(R.string.details_contains_ads))
} else {
add(stringResource(R.string.details_no_ads))
}
if (app.requiresGMS()) {
add(stringResource(R.string.details_gsf_dependent))
}
}
Row(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick)
.padding(
horizontal = dimensionResource(R.dimen.padding_small),
vertical = dimensionResource(R.dimen.padding_xsmall)
)
) {
AsyncImage( AsyncImage(
modifier = Modifier modifier = Modifier
.requiredSize(dimensionResource(R.dimen.icon_size_medium)) .requiredSize(dimensionResource(R.dimen.icon_size_medium))
@@ -86,26 +50,22 @@ fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Uni
contentDescription = null, contentDescription = null,
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )
}
)
}
Column( @Composable
modifier = Modifier private fun buildAppExtras(app: App): String = buildList {
.fillMaxWidth() add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString)
.padding(horizontal = dimensionResource(R.dimen.margin_small)) add("${app.labeledRating}")
) { add(stringResource(if (app.isFree) R.string.details_free else R.string.details_paid))
Text( add(
text = app.displayName, stringResource(
style = MaterialTheme.typography.bodyMedium, if (app.containsAds) R.string.details_contains_ads else R.string.details_no_ads
maxLines = 1,
overflow = TextOverflow.Ellipsis
) )
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) @PreviewWrapper(ThemePreviewProvider::class)
@Preview(showBackground = true) @Preview(showBackground = true)

View File

@@ -38,7 +38,7 @@ fun ExodusListItem(modifier: Modifier = Modifier, tracker: ExodusTracker) {
onClick = { context.browse(tracker.url) }, onClick = { context.browse(tracker.url) },
enabled = tracker.url.isNotBlank() enabled = tracker.url.isNotBlank()
) )
.padding(dimensionResource(R.dimen.padding_small)) .padding(dimensionResource(R.dimen.spacing_small))
) { ) {
Text( Text(
text = tracker.name, text = tracker.name,

View File

@@ -34,9 +34,9 @@ fun RatingListItem(modifier: Modifier = Modifier, label: String, rating: Float)
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.padding_small)), .padding(horizontal = dimensionResource(R.dimen.spacing_small)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
Text( Text(
text = label, text = label,

View File

@@ -45,8 +45,8 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) {
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) { ) {
AsyncImage( AsyncImage(
@@ -61,7 +61,7 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) {
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium))) .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
) )
Column( Column(
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)) modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_small))
) { ) {
Text( Text(
text = review.userName, text = review.userName,

View File

@@ -101,7 +101,7 @@ private fun ScreenContent(onAboutAurora: () -> Unit = {}) {
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(paddingValues), .padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
stickyHeader { stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) { Surface(modifier = Modifier.fillMaxWidth()) {
@@ -128,9 +128,9 @@ private fun BrandHeader() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.margin_medium)), .padding(dimensionResource(R.dimen.spacing_medium)),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)

View File

@@ -113,7 +113,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_small)), .padding(vertical = dimensionResource(R.dimen.spacing_small)),
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
AssistHeader() AssistHeader()
@@ -121,7 +121,7 @@ private fun ScreenContent(
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
@@ -153,7 +153,9 @@ private fun ScreenContent(
.fillMaxWidth() .fillMaxWidth()
.padding(bottom = dimensionResource(R.dimen.height_bottom_adj)), .padding(bottom = dimensionResource(R.dimen.height_bottom_adj)),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.spacing_medium)
)
) { ) {
Text( Text(
text = stringResource(R.string.account_logout), text = stringResource(R.string.account_logout),
@@ -166,7 +168,7 @@ private fun ScreenContent(
painter = painterResource(R.drawable.ic_logout), painter = painterResource(R.drawable.ic_logout),
contentDescription = null contentDescription = null
) )
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.padding_xsmall))) Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing_xsmall)))
Text( Text(
text = stringResource(R.string.action_logout) text = stringResource(R.string.action_logout)
) )
@@ -188,8 +190,8 @@ private fun AssistHeader() {
LazyRow( LazyRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = links.keys.toList(), key = { item -> item }) { label -> items(items = links.keys.toList(), key = { item -> item }) { label ->
AssistChip( AssistChip(

View File

@@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper 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.data.models.StreamCluster
import com.aurora.gplayapi.helpers.contracts.TopChartsContract import com.aurora.gplayapi.helpers.contracts.TopChartsContract
import com.aurora.store.R 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.ShimmerAppRow
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.composition.collectForced import com.aurora.store.compose.composition.collectForced
@@ -121,7 +122,7 @@ private fun TopChartsBody(
) { ) {
SecondaryScrollableTabRow( SecondaryScrollableTabRow(
selectedTabIndex = selectedIndex, selectedTabIndex = selectedIndex,
edgePadding = dimensionResource(R.dimen.padding_small) edgePadding = dimensionResource(R.dimen.spacing_small)
) { ) {
chartTitles.forEachIndexed { index, titleRes -> chartTitles.forEachIndexed { index, titleRes ->
Tab( Tab(
@@ -133,18 +134,18 @@ private fun TopChartsBody(
} }
when { when {
state is ViewState.Error -> EmptyState( state is ViewState.Error -> Placeholder(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
icon = R.drawable.ic_apps, painter = painterResource(R.drawable.ic_apps),
message = R.string.no_apps_available, message = stringResource(R.string.no_apps_available),
actionLabel = R.string.action_retry, actionLabel = stringResource(R.string.action_retry),
onAction = onRetry onAction = onRetry
) )
cluster != null && cluster.clusterAppList.isEmpty() -> EmptyState( cluster != null && cluster.clusterAppList.isEmpty() -> Placeholder(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
icon = R.drawable.ic_apps, painter = painterResource(R.drawable.ic_apps),
message = R.string.no_apps_available message = stringResource(R.string.no_apps_available)
) )
cluster != null -> { cluster != null -> {
@@ -155,10 +156,10 @@ private fun TopChartsBody(
.fillMaxWidth(), .fillMaxWidth(),
state = listState, state = listState,
contentPadding = PaddingValues( contentPadding = PaddingValues(
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
), ),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_xsmall) dimensionResource(R.dimen.spacing_xsmall)
) )
) { ) {
items(count = apps.size, key = { apps[it].id }) { index -> items(count = apps.size, key = { apps[it].id }) { index ->
@@ -172,7 +173,7 @@ private fun TopChartsBody(
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_large)), .padding(dimensionResource(R.dimen.spacing_large)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
@@ -186,8 +187,10 @@ private fun TopChartsBody(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxWidth(), .fillMaxWidth(),
contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.padding_small)), contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.spacing_small)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xsmall)) verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.spacing_xsmall)
)
) { ) {
items(8) { ShimmerAppRow() } items(8) { ShimmerAppRow() }
} }

View File

@@ -248,8 +248,8 @@ private fun ScreenContent(
.fillMaxWidth() .fillMaxWidth()
.clickable { onRequestSearch(pkg.displayName) } .clickable { onRequestSearch(pkg.displayName) }
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) )
} }
@@ -298,7 +298,7 @@ private fun ScreenContent(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
state = listState, state = listState,
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_xxsmall) dimensionResource(R.dimen.spacing_xsmall)
) )
) { ) {
if (selectedPackages.isNotEmpty()) { if (selectedPackages.isNotEmpty()) {

View File

@@ -15,7 +15,7 @@ import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.gplayapi.data.models.StreamBundle import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.store.R 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.StreamCarousel
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composition.collectForced import com.aurora.store.compose.composition.collectForced
@@ -40,7 +40,7 @@ fun CategoryBrowseScreen(
topBar = { TopAppBar(title = title) } topBar = { TopAppBar(title = title) }
) { paddingValues -> ) { paddingValues ->
if (uiState is ViewState.Error) { if (uiState is ViewState.Error) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)

View File

@@ -22,7 +22,7 @@ import androidx.paging.LoadState
import androidx.paging.compose.collectAsLazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -54,7 +54,7 @@ fun ExpandedStreamBrowseScreen(
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> { is LoadState.Error -> {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)
@@ -63,7 +63,7 @@ fun ExpandedStreamBrowseScreen(
else -> { else -> {
if (apps.itemCount == 0) { if (apps.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
@@ -74,7 +74,7 @@ fun ExpandedStreamBrowseScreen(
.fillMaxSize() .fillMaxSize()
.padding(paddingValues), .padding(paddingValues),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items( items(

View File

@@ -93,8 +93,8 @@ fun MoreSheet(
Column( Column(
Modifier.padding( Modifier.padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_small),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) { ) {
mainItems.forEach { item -> mainItems.forEach { item ->
@@ -119,8 +119,8 @@ fun MoreSheet(
Column( Column(
Modifier.padding( Modifier.padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.spacing_small),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) { ) {
extraItems.forEach { item -> extraItems.forEach { item ->
@@ -153,7 +153,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () ->
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_large)), .padding(dimensionResource(R.dimen.spacing_large)),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
AsyncImage( AsyncImage(
@@ -171,7 +171,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () ->
Column( Column(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.padding(horizontal = dimensionResource(R.dimen.padding_normal)) .padding(horizontal = dimensionResource(R.dimen.spacing_medium))
) { ) {
Text( Text(
text = if (isAnonymous) { text = if (isAnonymous) {

View File

@@ -49,8 +49,8 @@ fun NetworkSheet() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.padding_xlarge)) .padding(horizontal = dimensionResource(R.dimen.spacing_xlarge))
.padding(bottom = dimensionResource(R.dimen.padding_large)), .padding(bottom = dimensionResource(R.dimen.spacing_large)),
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
Icon( Icon(
@@ -58,17 +58,17 @@ fun NetworkSheet() {
contentDescription = null, contentDescription = null,
modifier = Modifier.requiredSize(48.dp) 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(
text = stringResource(R.string.title_no_network), text = stringResource(R.string.title_no_network),
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge
) )
Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_small))) Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_small)))
Text( Text(
text = stringResource(R.string.check_connectivity), text = stringResource(R.string.check_connectivity),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_xlarge))) Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_xlarge)))
Button( Button(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
onClick = { onClick = {

View File

@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.StreamCluster import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -73,7 +73,7 @@ private fun ScreenContent(
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> { is LoadState.Error -> {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)
@@ -82,7 +82,7 @@ private fun ScreenContent(
else -> { else -> {
if (apps.itemCount == 0) { if (apps.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
@@ -93,7 +93,7 @@ private fun ScreenContent(
.fillMaxSize() .fillMaxSize()
.padding(paddingValues), .padding(paddingValues),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items( items(

View File

@@ -66,8 +66,7 @@ import com.aurora.store.ComposeActivity
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ClusterRow import com.aurora.store.compose.composable.ClusterRow
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.Header
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.SectionHeader import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.composable.ShimmerCarouselSection import com.aurora.store.compose.composable.ShimmerCarouselSection
@@ -212,7 +211,7 @@ private fun ScreenContentError(message: String? = null) {
Scaffold( Scaffold(
topBar = { TopAppBar() } topBar = { TopAppBar() }
) { paddingValues -> ) { paddingValues ->
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_apps_outage), painter = painterResource(R.drawable.ic_apps_outage),
message = message ?: stringResource(R.string.toast_app_unavailable) message = message ?: stringResource(R.string.toast_app_unavailable)
@@ -426,7 +425,7 @@ private fun ScreenContentApp(
modifier = Modifier modifier = Modifier
.fillMaxSize(), .fillMaxSize(),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
), ),
state = listState state = listState
) { ) {
@@ -451,7 +450,7 @@ private fun ScreenContentApp(
} }
item { item {
Header( SectionHeader(
title = stringResource(R.string.details_more_about_app), title = stringResource(R.string.details_more_about_app),
subtitle = app.shortDescription, subtitle = app.shortDescription,
onClick = { showExtraPane(ExtraScreen.More) } onClick = { showExtraPane(ExtraScreen.More) }
@@ -487,7 +486,7 @@ private fun ScreenContentApp(
} }
item { item {
Header( SectionHeader(
title = stringResource(R.string.details_permission), title = stringResource(R.string.details_permission),
subtitle = if (app.permissions.isNotEmpty()) { subtitle = if (app.permissions.isNotEmpty()) {
stringResource(R.string.permissions_requested, app.permissions.size) 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.extensions.isWindowCompact
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.Header
import com.aurora.store.compose.composable.ScrollHint 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.TopAppBar
import com.aurora.store.compose.composable.details.ExodusListItem import com.aurora.store.compose.composable.details.ExodusListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -128,12 +128,12 @@ private fun ScreenContentReport(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
state = listState state = listState
) { ) {
stickyHeader { stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) { Surface(modifier = Modifier.fillMaxWidth()) {
Header( SectionHeader(
title = if (report?.trackers.isNullOrEmpty()) { title = if (report?.trackers.isNullOrEmpty()) {
stringResource(R.string.exodus_no_tracker) stringResource(R.string.exodus_no_tracker)
} else { } else {
@@ -178,11 +178,11 @@ private fun ScreenContentError(
) )
} }
) { paddingValues -> ) { paddingValues ->
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.failed_to_fetch_report), message = stringResource(R.string.failed_to_fetch_report),
actionMessage = stringResource(R.string.action_request_analysis), actionLabel = stringResource(R.string.action_request_analysis),
onAction = onRequestAnalysis onAction = onRequestAnalysis
) )
} }

View File

@@ -139,12 +139,14 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium)), .padding(dimensionResource(R.dimen.spacing_medium)),
verticalArrangement = Arrangement.SpaceBetween verticalArrangement = Arrangement.SpaceBetween
) { ) {
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.spacing_medium)
)
) { ) {
Info( Info(
painter = painterResource(R.drawable.ic_download_manager), painter = painterResource(R.drawable.ic_download_manager),
@@ -181,7 +183,7 @@ private fun ScreenContent(
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
FilledTonalButton( FilledTonalButton(

View File

@@ -103,10 +103,10 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.navigationBarsPadding() .navigationBarsPadding()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)) .padding(horizontal = dimensionResource(R.dimen.spacing_medium))
.padding(vertical = dimensionResource(R.dimen.padding_small)), .padding(vertical = dimensionResource(R.dimen.spacing_small)),
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
FilledTonalButton( FilledTonalButton(
@@ -147,7 +147,7 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
state = listState state = listState
) { ) {
item { item {

View File

@@ -38,9 +38,9 @@ import com.aurora.extensions.adaptiveNavigationIcon
import com.aurora.extensions.isWindowCompact import com.aurora.extensions.isWindowCompact
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.ScrollHint 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.TopAppBar
import com.aurora.store.compose.composable.app.AppListItem import com.aurora.store.compose.composable.app.AppListItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -100,16 +100,18 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
state = listState, state = listState,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.spacing_medium)
)
) { ) {
item { item {
Header(title = stringResource(R.string.details_description)) SectionHeader(title = stringResource(R.string.details_description))
} }
item { item {
Text( Text(
modifier = Modifier.padding( modifier = Modifier.padding(
horizontal = dimensionResource(R.dimen.padding_medium) horizontal = dimensionResource(R.dimen.spacing_medium)
), ),
text = AnnotatedString.fromHtml( text = AnnotatedString.fromHtml(
htmlString = app.description htmlString = app.description
@@ -144,7 +146,7 @@ private fun ScreenContent(
*/ */
@Composable @Composable
private fun AppDependencies(dependencies: List<App>, onNavigateTo: (Destination) -> Unit) { 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()) { if (dependencies.isEmpty()) {
Info( Info(
title = AnnotatedString(text = stringResource(R.string.details_no_dependencies)) title = AnnotatedString(text = stringResource(R.string.details_no_dependencies))
@@ -166,7 +168,7 @@ private fun AppDependencies(dependencies: List<App>, onNavigateTo: (Destination)
*/ */
@Composable @Composable
private fun AppInfoMore(app: App) { private fun AppInfoMore(app: App) {
Header(title = stringResource(R.string.details_more_info)) SectionHeader(title = stringResource(R.string.details_more_info))
Info( Info(
title = AnnotatedString( title = AnnotatedString(
text = stringResource(R.string.details_more_package_name) text = stringResource(R.string.details_more_package_name)

View File

@@ -94,9 +94,11 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
state = listState, 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 -> items(items = permissionsInfo.keys.toList(), key = { it }) { permission ->
val permissionInfo = permissionsInfo.getValue(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.gplayapi.data.models.Review
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.details.ReviewListItem import com.aurora.store.compose.composable.details.ReviewListItem
@@ -114,7 +114,7 @@ private fun ScreenContent(
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> { is LoadState.Error -> {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)
@@ -167,8 +167,8 @@ private fun FilterHeader(onClick: (filter: Review.Filter) -> Unit) {
LazyRow( LazyRow(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.margin_normal)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = filters.keys.toList(), key = { item -> item }) { filter -> items(items = filters.keys.toList(), key = { item -> item }) { filter ->
val selected = activeFilter == filter val selected = activeFilter == filter

View File

@@ -53,8 +53,8 @@ fun Actions(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium))), .padding(PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium))),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
val buttonWidthModifier = when { val buttonWidthModifier = when {
windowAdaptiveInfo.isWindowCompact -> Modifier.weight(1F) windowAdaptiveInfo.isWindowCompact -> Modifier.weight(1F)
@@ -92,7 +92,7 @@ fun Actions(
@Composable @Composable
private fun ActionsPreview() { private fun ActionsPreview() {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Actions( Actions(
primaryActionDisplayName = stringResource(R.string.action_install), 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 androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -38,11 +38,11 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
*/ */
@Composable @Composable
fun Changelog(changelog: String) { fun Changelog(changelog: String) {
Header(title = stringResource(R.string.details_changelog)) SectionHeader(title = stringResource(R.string.details_changelog))
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)) .padding(dimensionResource(R.dimen.spacing_medium))
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@@ -50,8 +50,8 @@ fun Changelog(changelog: String) {
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small))) .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(color = MaterialTheme.colorScheme.secondaryContainer) .background(color = MaterialTheme.colorScheme.secondaryContainer)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) { ) {
Text( Text(
@@ -71,7 +71,7 @@ fun Changelog(changelog: String) {
@Composable @Composable
private fun ChangelogPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun ChangelogPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Changelog(changelog = app.changes) 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.theme.successColor import com.aurora.store.compose.theme.successColor
import com.aurora.store.compose.theme.warningColor import com.aurora.store.compose.theme.warningColor
@@ -30,7 +30,7 @@ import com.aurora.store.data.model.Scores
*/ */
@Composable @Composable
fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) { fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) {
Header( SectionHeader(
title = stringResource(R.string.details_compatibility_title), title = stringResource(R.string.details_compatibility_title),
subtitle = stringResource(R.string.plexus_powered) subtitle = stringResource(R.string.plexus_powered)
) )
@@ -82,7 +82,7 @@ fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) {
@Composable @Composable
private fun CompatibilityPreview() { private fun CompatibilityPreview() {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Compatibility(needsGms = true) 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.EntryType
import com.aurora.gplayapi.data.models.datasafety.Report import com.aurora.gplayapi.data.models.datasafety.Report
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -37,7 +37,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
fun DataSafety(report: Report, privacyPolicyUrl: String) { fun DataSafety(report: Report, privacyPolicyUrl: String) {
val context = LocalContext.current val context = LocalContext.current
Header( SectionHeader(
title = stringResource(R.string.details_data_safety_title), title = stringResource(R.string.details_data_safety_title),
subtitle = stringResource(R.string.details_data_safety_subtitle), subtitle = stringResource(R.string.details_data_safety_subtitle),
onClick = { context.browse(privacyPolicyUrl) } onClick = { context.browse(privacyPolicyUrl) }
@@ -86,7 +86,7 @@ fun DataSafety(report: Report, privacyPolicyUrl: String) {
@Composable @Composable
private fun DataSafetyPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun DataSafetyPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
DataSafety( DataSafety(
privacyPolicyUrl = app.privacyPolicyUrl, privacyPolicyUrl = app.privacyPolicyUrl,

View File

@@ -109,7 +109,7 @@ fun Details(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.margin_medium)) .padding(dimensionResource(R.dimen.spacing_medium))
) { ) {
AnimatedAppIcon( AnimatedAppIcon(
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)),
@@ -117,7 +117,7 @@ fun Details(
inProgress = state.inProgress(), inProgress = state.inProgress(),
progress = state.progress() 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(
text = app.displayName, text = app.displayName,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
@@ -172,7 +172,7 @@ fun Details(
@Composable @Composable
private fun DetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun DetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Details(app = app) Details(app = app)
} }

View File

@@ -22,8 +22,8 @@ import com.aurora.extensions.copyToClipBoard
import com.aurora.extensions.mailTo import com.aurora.extensions.mailTo
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider 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) { fun DeveloperDetails(address: String, website: String, email: String) {
val context = LocalContext.current val context = LocalContext.current
Header(title = stringResource(R.string.details_dev_details)) SectionHeader(title = stringResource(R.string.details_dev_details))
Column { Column {
if (website.isNotBlank()) { if (website.isNotBlank()) {
Info( Info(
@@ -74,7 +74,7 @@ fun DeveloperDetails(address: String, website: String, email: String) {
@Composable @Composable
private fun DeveloperDetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun DeveloperDetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
DeveloperDetails( DeveloperDetails(
address = app.developerAddress, 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.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.theme.successColor import com.aurora.store.compose.theme.successColor
import com.aurora.store.compose.theme.warningColor import com.aurora.store.compose.theme.warningColor
@@ -30,7 +30,7 @@ import com.aurora.store.data.model.Report
*/ */
@Composable @Composable
fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) { fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) {
Header( SectionHeader(
title = stringResource(R.string.details_privacy), title = stringResource(R.string.details_privacy),
subtitle = stringResource(R.string.exodus_powered), subtitle = stringResource(R.string.exodus_powered),
onClick = onNavigateToDetailsExodus onClick = onNavigateToDetailsExodus
@@ -64,7 +64,7 @@ fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) {
@Composable @Composable
private fun PrivacyPreview() { private fun PrivacyPreview() {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Privacy(report = Report(), onNavigateToDetailsExodus = {}) 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.Rating
import com.aurora.gplayapi.data.models.Review import com.aurora.gplayapi.data.models.Review
import com.aurora.store.R 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.RatingListItem
import com.aurora.store.compose.composable.details.ReviewListItem import com.aurora.store.compose.composable.details.ReviewListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -82,7 +82,7 @@ fun RatingAndReviews(
} }
} }
Header( SectionHeader(
title = stringResource(R.string.details_ratings), title = stringResource(R.string.details_ratings),
onClick = onNavigateToDetailsReview onClick = onNavigateToDetailsReview
) )
@@ -90,12 +90,12 @@ fun RatingAndReviews(
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)), .padding(dimensionResource(R.dimen.spacing_medium)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_large)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
) { ) {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)), modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
@@ -114,7 +114,7 @@ fun RatingAndReviews(
} }
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
stars.reversed().fastForEach { star -> stars.reversed().fastForEach { star ->
RatingListItem( RatingListItem(
@@ -129,8 +129,8 @@ fun RatingAndReviews(
val pagerState = rememberPagerState { featuredReviews.size } val pagerState = rememberPagerState { featuredReviews.size }
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
pageSpacing = dimensionResource(R.dimen.margin_medium) pageSpacing = dimensionResource(R.dimen.spacing_medium)
) { page -> ) { page ->
Box( Box(
modifier = Modifier modifier = Modifier
@@ -158,7 +158,7 @@ private fun RatingAndReviewsPreview(@PreviewParameter(AppPreviewProvider::class)
) )
} }
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
RatingAndReviews(rating = app.rating, featuredReviews = reviews) RatingAndReviews(rating = app.rating, featuredReviews = reviews)
} }

View File

@@ -37,8 +37,8 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
@Composable @Composable
fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int) -> Unit = {}) { fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int) -> Unit = {}) {
LazyRow( LazyRow(
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
items(items = screenshots, key = { artwork -> artwork.url }) { artwork -> items(items = screenshots, key = { artwork -> artwork.url }) { artwork ->
ScreenshotListItem( ScreenshotListItem(
@@ -57,7 +57,7 @@ fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int)
@Composable @Composable
private fun ScreenshotsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun ScreenshotsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Screenshots(screenshots = app.screenshots) Screenshots(screenshots = app.screenshots)
} }

View File

@@ -63,8 +63,8 @@ fun Tags(app: App) {
).filterKeys { it != null } ).filterKeys { it != null }
LazyRow( LazyRow(
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = tags.keys.toList()) { label -> items(items = tags.keys.toList()) { label ->
TagListItem(label = label!!, painter = painterResource(tags.getValue(label))) TagListItem(label = label!!, painter = painterResource(tags.getValue(label)))
@@ -77,7 +77,7 @@ fun Tags(app: App) {
@Composable @Composable
private fun TagsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun TagsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Tags(app = app) Tags(app = app)
} }

View File

@@ -26,8 +26,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R 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.Info
import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -40,13 +40,13 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
*/ */
@Composable @Composable
fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) { fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) {
Header(title = stringResource(R.string.details_beta)) SectionHeader(title = stringResource(R.string.details_beta))
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)), .padding(dimensionResource(R.dimen.spacing_medium)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
) { ) {
Info( Info(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
@@ -79,7 +79,7 @@ fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Bool
@Composable @Composable
private fun TestingPreview(@PreviewParameter(AppPreviewProvider::class) app: App) { private fun TestingPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
Column( Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
Testing(isSubscribed = app.testingProgram!!.isSubscribed) 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.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -96,7 +96,7 @@ private fun ScreenContent(
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> { is LoadState.Error -> {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)
@@ -108,9 +108,9 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)), .padding(vertical = dimensionResource(R.dimen.spacing_medium)),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items(count = apps.itemCount, key = apps.itemKey { it.id }) { index -> 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.extensions.copyToClipBoard
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.DispenserListItem 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.composable.TopAppBar
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.viewmodel.dispenser.DispenserViewModel import com.aurora.store.viewmodel.dispenser.DispenserViewModel
@@ -94,11 +94,11 @@ private fun ScreenContent(
} }
) { paddingValues -> ) { paddingValues ->
if (dispensers.isEmpty()) { if (dispensers.isEmpty()) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_server), painter = painterResource(R.drawable.ic_server),
message = stringResource(R.string.no_dispensers_available), message = stringResource(R.string.no_dispensers_available),
actionMessage = stringResource(R.string.add_dispenser_title), actionLabel = stringResource(R.string.add_dispenser_title),
onAction = onAddDispenser onAction = onAddDispenser
) )
} else { } else {
@@ -106,7 +106,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = dispensers.toList(), key = { url -> url }) { url -> items(items = dispensers.toList(), key = { url -> url }) { url ->
DispenserListItem( DispenserListItem(

View File

@@ -53,7 +53,9 @@ fun InputDispenserDialog(onAdd: (url: String) -> Unit = {}, onDismiss: () -> Uni
title = { Text(text = stringResource(R.string.add_dispenser_title)) }, title = { Text(text = stringResource(R.string.add_dispenser_title)) },
text = { text = {
Column( 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)) Text(text = stringResource(R.string.add_dispenser_summary))
OutlinedTextField( OutlinedTextField(

View File

@@ -41,7 +41,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.DownloadListItem 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.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination 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.preview.ThemePreviewProvider
import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu
import com.aurora.store.compose.ui.downloads.menu.MenuItem 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.model.DownloadStatus
import com.aurora.store.data.room.download.Download import com.aurora.store.data.room.download.Download
import com.aurora.store.viewmodel.downloads.DownloadsViewModel 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. * Save the initial loading state to make sure we don't replay the loading animation again.
*/ */
var initialLoad by rememberSaveable { mutableStateOf(true) } 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 @Composable
fun SetupMenu() { fun SetupMenu() {
@@ -138,7 +152,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.spacing_medium))
) { ) {
when { when {
downloads.loadState.refresh is LoadState.Loading && initialLoad -> { downloads.loadState.refresh is LoadState.Loading && initialLoad -> {
@@ -149,7 +163,7 @@ private fun ScreenContent(
initialLoad = false initialLoad = false
if (downloads.itemCount == 0) { if (downloads.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_download_manager), painter = painterResource(R.drawable.ic_download_manager),
message = stringResource(R.string.download_none) message = stringResource(R.string.download_none)
@@ -168,15 +182,7 @@ private fun ScreenContent(
DownloadListItem( DownloadListItem(
modifier = Modifier.animateItem(), modifier = Modifier.animateItem(),
download = download, download = download,
onClick = { onClick = { actionsTarget = download }
onNavigateTo(
Destination.AppDetails(download.packageName)
)
},
onClear = { onClear(download) },
onCancel = { onCancel(download.packageName) },
onExport = { onExport(download) },
onInstall = { onInstall(download) }
) )
} }
} }

View File

@@ -40,8 +40,8 @@ import com.aurora.extensions.emptyPagingItems
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.FavouriteListItem
import com.aurora.store.compose.composable.Placeholder
import com.aurora.store.compose.composable.ScrollHint import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -140,7 +140,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.spacing_medium))
) { ) {
when { when {
favourites.loadState.refresh is LoadState.Loading && initialLoad -> { favourites.loadState.refresh is LoadState.Loading && initialLoad -> {
@@ -151,7 +151,7 @@ private fun ScreenContent(
initialLoad = false initialLoad = false
if (favourites.itemCount == 0) { if (favourites.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_favorite_unchecked), painter = painterResource(R.drawable.ic_favorite_unchecked),
message = stringResource(R.string.details_no_favourites) 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.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
@@ -95,7 +95,7 @@ private fun ScreenContent(
initialLoad = false initialLoad = false
if (apps.itemCount == 0) { if (apps.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_apps_outage), painter = painterResource(R.drawable.ic_apps_outage),
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
@@ -106,7 +106,7 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
state = listState, state = listState,
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items( items(

View File

@@ -61,12 +61,12 @@ private fun ScreenContent(
LazyColumn( LazyColumn(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
state = listState state = listState
) { ) {
item { item {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_title_gsf), text = stringResource(R.string.onboarding_title_gsf),

View File

@@ -99,16 +99,16 @@ private fun ScreenContent(
.fillMaxWidth() .fillMaxWidth()
.navigationBarsPadding() .navigationBarsPadding()
.padding( .padding(
vertical = dimensionResource(R.dimen.padding_medium), vertical = dimensionResource(R.dimen.spacing_medium),
horizontal = if (windowAdaptiveInfo.isWindowCompact) { horizontal = if (windowAdaptiveInfo.isWindowCompact) {
dimensionResource(R.dimen.padding_medium) dimensionResource(R.dimen.spacing_medium)
} else { } else {
dimensionResource(R.dimen.padding_xlarge) dimensionResource(R.dimen.spacing_xlarge)
} }
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_medium), dimensionResource(R.dimen.spacing_medium),
Alignment.End Alignment.End
) )
) { ) {

View File

@@ -45,12 +45,12 @@ private fun PageContent(
onPermissionCallback: (type: PermissionType) -> Unit = {} onPermissionCallback: (type: PermissionType) -> Unit = {}
) { ) {
PermissionList( PermissionList(
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.padding_medium)), modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
permissions = permissions, permissions = permissions,
onPermissionCallback = onPermissionCallback, onPermissionCallback = onPermissionCallback,
header = { header = {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_title_permissions), text = stringResource(R.string.onboarding_title_permissions),

View File

@@ -96,12 +96,12 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
stickyHeader { stickyHeader {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_title_welcome), text = stringResource(R.string.onboarding_title_welcome),

View File

@@ -76,7 +76,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = availableInstallers, key = { i -> i.id }) { installerInfo -> items(items = availableInstallers, key = { i -> i.id }) { installerInfo ->
InstallerListItem( InstallerListItem(

View File

@@ -228,7 +228,7 @@ private fun ProxyURLDialog(
Column { Column {
Text( Text(
text = stringResource(R.string.pref_network_proxy_url_message), 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( OutlinedTextField(
value = url, value = url,
@@ -255,7 +255,7 @@ private fun ProxyURLDialog(
Text(stringResource(R.string.disable)) Text(stringResource(R.string.disable))
} }
} }
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
TextButton(onClick = onDismiss) { TextButton(onClick = onDismiss) {
Text(stringResource(android.R.string.cancel)) Text(stringResource(android.R.string.cancel))
} }
@@ -286,14 +286,14 @@ internal fun SingleChoiceDialog(
onClick = { onSelect(index) }, onClick = { onSelect(index) },
role = Role.RadioButton role = Role.RadioButton
) )
.padding(vertical = dimensionResource(R.dimen.padding_small)), .padding(vertical = dimensionResource(R.dimen.spacing_small)),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
RadioButton( RadioButton(
selected = index == selected, selected = index == selected,
onClick = null onClick = null
) )
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small))) Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
Text(option) Text(option)
} }
} }

View File

@@ -366,7 +366,7 @@ private fun UpdatesRestrictionsDialog(onUpdateAutomatedCheck: () -> Unit, onDism
text = { text = {
Column { Column {
Text(stringResource(R.string.pref_updates_restrictions_desc)) 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( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@@ -74,7 +74,7 @@ import com.aurora.gplayapi.SearchSuggestEntry
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator 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.ScrollHint
import com.aurora.store.compose.composable.SearchSuggestionListItem import com.aurora.store.compose.composable.SearchSuggestionListItem
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
@@ -238,7 +238,7 @@ private fun ScreenContent(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(vertical = dimensionResource(R.dimen.padding_medium)) .padding(vertical = dimensionResource(R.dimen.spacing_medium))
) { ) {
FilterHeader( FilterHeader(
isEnabled = isSearching && results.loadState.refresh is LoadState.NotLoading, isEnabled = isSearching && results.loadState.refresh is LoadState.NotLoading,
@@ -250,7 +250,7 @@ private fun ScreenContent(
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
is LoadState.Error -> { is LoadState.Error -> {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error) message = stringResource(R.string.error)
@@ -259,7 +259,7 @@ private fun ScreenContent(
else -> { else -> {
if (isSearching && results.itemCount == 0) { if (isSearching && results.itemCount == 0) {
Error( Placeholder(
modifier = Modifier.padding(paddingValues), modifier = Modifier.padding(paddingValues),
painter = painterResource(R.drawable.ic_disclaimer), painter = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
@@ -271,7 +271,7 @@ private fun ScreenContent(
state = listState, state = listState,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items( items(
@@ -316,7 +316,7 @@ private fun ScreenContent(
else -> { else -> {
if (isSearching && results.itemCount > 0) { if (isSearching && results.itemCount > 0) {
Error( Placeholder(
painter = painterResource(R.drawable.ic_round_search), painter = painterResource(R.drawable.ic_round_search),
message = stringResource(R.string.select_app_for_details) message = stringResource(R.string.select_app_for_details)
) )
@@ -435,8 +435,8 @@ private fun FilterHeader(
LazyRow( LazyRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
items(items = filters, key = { item -> item }) { filter -> items(items = filters, key = { item -> item }) { filter ->
val isSelected = when (filter) { val isSelected = when (filter) {

View File

@@ -82,7 +82,7 @@ fun AppUpdateSheet(
HorizontalDivider( HorizontalDivider(
modifier = Modifier.padding( modifier = Modifier.padding(
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) )
@@ -107,7 +107,7 @@ fun AppUpdateSheet(
HorizontalDivider( HorizontalDivider(
modifier = Modifier.padding( 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 modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_xsmall) vertical = dimensionResource(R.dimen.spacing_xsmall)
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
) { ) {
AsyncImage( AsyncImage(
model = ImageRequest.Builder(LocalContext.current) model = ImageRequest.Builder(LocalContext.current)
@@ -212,12 +212,12 @@ private fun Item(label: String, onClick: () -> Unit) {
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = dimensionResource(R.dimen.padding_xsmall)) .padding(horizontal = dimensionResource(R.dimen.spacing_xsmall))
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_normal))) .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
.clickable(onClick = onClick) .clickable(onClick = onClick)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_small) vertical = dimensionResource(R.dimen.spacing_small)
) )
) )
} }
@@ -228,8 +228,8 @@ private fun ChangelogSection(html: String) {
text = stringResource(R.string.details_changelog), text = stringResource(R.string.details_changelog),
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
modifier = Modifier.padding( modifier = Modifier.padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_xsmall) vertical = dimensionResource(R.dimen.spacing_xsmall)
) )
) )
@@ -238,8 +238,8 @@ private fun ChangelogSection(html: String) {
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding( modifier = Modifier.padding(
horizontal = dimensionResource(R.dimen.padding_medium), horizontal = dimensionResource(R.dimen.spacing_medium),
vertical = dimensionResource(R.dimen.padding_xsmall) 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 modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(0.1f) .weight(0.1f)
.padding(horizontal = dimensionResource(R.dimen.padding_xlarge)), .padding(horizontal = dimensionResource(R.dimen.spacing_xlarge)),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
val (statusText, statusColor) = when { val (statusText, statusColor) = when {
@@ -260,7 +260,7 @@ fun SplashScreen(
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_small) dimensionResource(R.dimen.spacing_small)
) )
) { ) {
Button( Button(

View File

@@ -53,7 +53,7 @@ private fun PageContent(
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
stickyHeader { stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) { Surface(modifier = Modifier.fillMaxWidth()) {

View File

@@ -52,7 +52,7 @@ private fun PageContent(
) { ) {
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
) { ) {
stickyHeader { stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) { 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.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items 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.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -17,12 +19,13 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.store.R 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.SectionHeaderWithAction import com.aurora.store.compose.composable.SectionHeader
import com.aurora.store.compose.composable.ShimmerUpdateItem import com.aurora.store.compose.composable.ShimmerUpdateItem
import com.aurora.store.compose.composable.app.AppUpdateItem import com.aurora.store.compose.composable.app.AppUpdateItem
import com.aurora.store.compose.navigation.Destination import com.aurora.store.compose.navigation.Destination
@@ -82,7 +85,7 @@ fun UpdatesScreen(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
items(10) { ShimmerUpdateItem() } items(10) { ShimmerUpdateItem() }
@@ -90,11 +93,11 @@ fun UpdatesScreen(
} }
updateMap.isEmpty() -> { updateMap.isEmpty() -> {
EmptyState( Placeholder(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
icon = R.drawable.ic_updates, painter = painterResource(R.drawable.ic_updates),
message = R.string.details_no_updates, message = stringResource(R.string.details_no_updates),
actionLabel = R.string.check_updates, actionLabel = stringResource(R.string.check_updates),
onAction = { viewModel.fetchUpdates() } onAction = { viewModel.fetchUpdates() }
) )
} }
@@ -103,7 +106,7 @@ fun UpdatesScreen(
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy( verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_medium) dimensionResource(R.dimen.spacing_medium)
) )
) { ) {
if (mainEntries.isNotEmpty()) { if (mainEntries.isNotEmpty()) {
@@ -122,16 +125,19 @@ fun UpdatesScreen(
R.string.action_update_all R.string.action_update_all
} }
) )
SectionHeaderWithAction( SectionHeader(
title = title, title = title,
action = actionLabel, trailing = {
onAction = { TextButton(
onClick = {
if (mainAllEnqueued) { if (mainAllEnqueued) {
onCancelAll() onCancelAll()
} else { } else {
onRequestUpdateAll(mainEntries.map { it.key }) onRequestUpdateAll(mainEntries.map { it.key })
} }
} }
) { Text(actionLabel) }
}
) )
} }
updateItems( updateItems(
@@ -145,13 +151,16 @@ fun UpdatesScreen(
if (approvalEntries.isNotEmpty()) { if (approvalEntries.isNotEmpty()) {
item(key = "header_approval") { item(key = "header_approval") {
SectionHeaderWithAction( SectionHeader(
title = stringResource(R.string.updates_approval_header), title = stringResource(R.string.updates_approval_header),
subtitle = stringResource(R.string.updates_approval_desc), subtitle = stringResource(R.string.updates_approval_desc),
action = stringResource(R.string.action_update_all), trailing = {
onAction = { TextButton(
onClick = {
onRequestUpdateAll(approvalEntries.map { it.key }) onRequestUpdateAll(approvalEntries.map { it.key })
} }
) { Text(stringResource(R.string.action_update_all)) }
}
) )
} }
updateItems( updateItems(
@@ -165,7 +174,7 @@ fun UpdatesScreen(
if (incompatibleEntries.isNotEmpty()) { if (incompatibleEntries.isNotEmpty()) {
item(key = "header_incompatible") { item(key = "header_incompatible") {
SectionHeaderWithAction( SectionHeader(
title = stringResource(R.string.updates_incompatible_header), title = stringResource(R.string.updates_incompatible_header),
subtitle = stringResource(R.string.updates_incompatible_desc) subtitle = stringResource(R.string.updates_incompatible_desc)
) )
@@ -181,7 +190,7 @@ fun UpdatesScreen(
if (ignoredUpdates.isNotEmpty()) { if (ignoredUpdates.isNotEmpty()) {
item(key = "header_ignored") { item(key = "header_ignored") {
SectionHeaderWithAction( SectionHeader(
title = stringResource(R.string.updates_ignored_header), title = stringResource(R.string.updates_ignored_header),
subtitle = stringResource(R.string.updates_ignored_desc) subtitle = stringResource(R.string.updates_ignored_desc)
) )

View File

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

View File

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

View File

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

View File

@@ -18,21 +18,11 @@
--> -->
<resources> <resources>
<dimen name="padding_xxsmall">2dp</dimen> <dimen name="spacing_xsmall">4dp</dimen>
<dimen name="padding_xsmall">4dp</dimen> <dimen name="spacing_small">8dp</dimen>
<dimen name="padding_small">8dp</dimen> <dimen name="spacing_medium">12dp</dimen>
<dimen name="padding_medium">10dp</dimen> <dimen name="spacing_large">16dp</dimen>
<dimen name="padding_normal">12dp</dimen> <dimen name="spacing_xlarge">24dp</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="icon_size_default">24dp</dimen> <dimen name="icon_size_default">24dp</dimen>
<dimen name="icon_size_category">36dp</dimen> <dimen name="icon_size_category">36dp</dimen>
@@ -69,8 +59,7 @@
<dimen name="review_height">200dp</dimen> <dimen name="review_height">200dp</dimen>
<dimen name="radius_small">8dp</dimen> <dimen name="radius_small">8dp</dimen>
<dimen name="radius_medium">10dp</dimen> <dimen name="radius_medium">12dp</dimen>
<dimen name="radius_normal">12dp</dimen>
<dimen name="radius_large">16dp</dimen> <dimen name="radius_large">16dp</dimen>
<dimen name="scroll_hint_padding">5dp</dimen> <dimen name="scroll_hint_padding">5dp</dimen>