compose: housekeeping
This commit is contained in:
@@ -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)"
|
||||
)
|
||||
}
|
||||
@@ -8,23 +8,14 @@ package com.aurora.store.compose.composable
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.Color
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import androidx.core.graphics.drawable.toBitmap
|
||||
@@ -33,18 +24,6 @@ import com.aurora.store.BuildConfig
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
/**
|
||||
* Composable for displaying package details in a list for blacklisting
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param icon Icon for the package
|
||||
* @param displayName User-readable name of the package
|
||||
* @param packageName Name of the package
|
||||
* @param versionName versionName of the package
|
||||
* @param versionCode versionCode of the package
|
||||
* @param isChecked Whether the app is blacklisted
|
||||
* @param isEnabled Whether this app is allowed to be blacklisted
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun BlackListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -57,48 +36,25 @@ fun BlackListItem(
|
||||
isEnabled: Boolean = true,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(enabled = isEnabled, onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(modifier = Modifier.weight(1F)) {
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = displayName,
|
||||
supporting = packageName,
|
||||
tertiary = stringResource(R.string.version, versionName, versionCode),
|
||||
headlineStyle = MaterialTheme.typography.bodyMedium,
|
||||
onClick = onClick,
|
||||
enabled = isEnabled,
|
||||
leading = {
|
||||
Image(
|
||||
bitmap = icon.asImageBitmap(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
Text(
|
||||
text = displayName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = packageName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.version, versionName, versionCode),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
trailing = {
|
||||
Checkbox(checked = isChecked, enabled = isEnabled, onCheckedChange = { onClick() })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
|
||||
@@ -38,11 +38,11 @@ fun CategoryItem(modifier: Modifier = Modifier, category: Category, onClick: ()
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
|
||||
@@ -31,7 +31,7 @@ fun ContainedLoadingIndicator(modifier: Modifier = Modifier) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
.padding(dimensionResource(R.dimen.spacing_small)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val description = stringResource(R.string.loading)
|
||||
|
||||
@@ -5,34 +5,13 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Composable to display device details for spoofing in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param userReadableName Name of the device, obtained through `UserReadableName` property
|
||||
* @param manufacturer Name of the device manufacturer, obtained through `Build.MANUFACTURER` property
|
||||
* @param androidVersionSdk Android version on the device, obtained through `Build.VERSION.SDK_INT` property
|
||||
* @param platforms Platforms supported on the device, obtained through `Platforms` property
|
||||
* @param isChecked If the device is selected
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DeviceListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -43,34 +22,16 @@ fun DeviceListItem(
|
||||
isChecked: Boolean = false,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { if (!isChecked) onClick() })
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = userReadableName,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = platforms.replace(",\\s*".toRegex(), ", "),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = userReadableName,
|
||||
supporting = stringResource(R.string.spoof_property, manufacturer, androidVersionSdk),
|
||||
tertiary = platforms.replace(",\\s*".toRegex(), ", "),
|
||||
onClick = { if (!isChecked) onClick() },
|
||||
trailing = {
|
||||
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
||||
@@ -5,32 +5,17 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Composable to display dispenser URL in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param url URL of the dispenser
|
||||
* @param onClick Callback when this URL is clicked
|
||||
* @param onClear Callback when the clear button is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DispenserListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -38,29 +23,14 @@ fun DispenserListItem(
|
||||
onClick: () -> Unit = {},
|
||||
onClear: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.weight(1F),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_server),
|
||||
contentDescription = null
|
||||
)
|
||||
Text(
|
||||
text = url,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = url,
|
||||
onClick = onClick,
|
||||
leading = {
|
||||
Icon(painter = painterResource(R.drawable.ic_server), contentDescription = null)
|
||||
},
|
||||
trailing = {
|
||||
TextButton(onClick = onClear) {
|
||||
Text(
|
||||
text = stringResource(R.string.remove),
|
||||
@@ -69,6 +39,7 @@ fun DispenserListItem(
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
||||
@@ -8,36 +8,16 @@ package com.aurora.store.compose.composable
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import android.text.format.Formatter
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
@@ -49,138 +29,53 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.util.CommonUtil.getETAString
|
||||
import com.aurora.store.util.PackageUtil
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Composable to display details of a download in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param download [Download] to display
|
||||
* @param onClick Callback when this composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DownloadListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
download: Download,
|
||||
onClick: () -> Unit = {},
|
||||
onClear: () -> Unit = {},
|
||||
onCancel: () -> Unit = {},
|
||||
onExport: () -> Unit = {},
|
||||
onInstall: () -> Unit = {},
|
||||
isExpanded: Boolean = false
|
||||
) {
|
||||
val progress = "${download.progress}%"
|
||||
val speed = "${Formatter.formatShortFileSize(LocalContext.current, download.speed)}/s"
|
||||
val eta = getETAString(LocalContext.current, download.timeRemaining)
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var isVisible by remember { mutableStateOf(true) }
|
||||
var isExpanded by remember { mutableStateOf(isExpanded) }
|
||||
|
||||
fun requestClear() {
|
||||
coroutineScope.launch {
|
||||
isVisible = false
|
||||
delay(300) // Let the animation play
|
||||
onClear()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ExpandedMenu() {
|
||||
fun DownloadListItem(modifier: Modifier = Modifier, download: Download, onClick: () -> Unit = {}) {
|
||||
val context = LocalContext.current
|
||||
Box(modifier = modifier) {
|
||||
IconButton(onClick = { isExpanded = true }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_more_vert),
|
||||
contentDescription = stringResource(R.string.menu)
|
||||
)
|
||||
}
|
||||
DropdownMenu(expanded = isExpanded, onDismissRequest = { isExpanded = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.action_cancel)) },
|
||||
onClick = onCancel,
|
||||
enabled = download.isRunning
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.action_install)) },
|
||||
onClick = onInstall,
|
||||
enabled = download.canInstall(context)
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.action_export)) },
|
||||
onClick = onExport,
|
||||
enabled = download.canInstall(context) ||
|
||||
PackageUtil.isInstalled(
|
||||
context,
|
||||
download.packageName
|
||||
)
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.action_clear)) },
|
||||
onClick = ::requestClear,
|
||||
enabled = !download.isRunning
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val statusLine = stringResource(download.status.localized)
|
||||
val tertiaryLine = if (download.status in DownloadStatus.running) {
|
||||
val progress = "${download.progress}%"
|
||||
val speed = "${Formatter.formatShortFileSize(context, download.speed)}/s"
|
||||
val eta = getETAString(context, download.timeRemaining)
|
||||
"$progress • $speed • $eta"
|
||||
} else {
|
||||
DateUtils.formatDateTime(context, download.downloadedAt, DateUtils.FORMAT_SHOW_DATE)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = isVisible, exit = shrinkVertically() + fadeOut()) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(modifier = Modifier.weight(1F)) {
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = download.displayName,
|
||||
supporting = statusLine,
|
||||
tertiary = tertiaryLine,
|
||||
headlineStyle = MaterialTheme.typography.bodyMedium,
|
||||
onClick = onClick,
|
||||
leading = {
|
||||
AnimatedAppIcon(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)),
|
||||
iconUrl = download.iconURL,
|
||||
inProgress = download.isRunning,
|
||||
progress = download.progress.toFloat()
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
Text(
|
||||
text = download.displayName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
},
|
||||
trailing = if (download.isFinished) {
|
||||
{
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_check),
|
||||
contentDescription = stringResource(R.string.download_completed),
|
||||
tint = Color(0xFF4CAF50)
|
||||
)
|
||||
Text(
|
||||
text = stringResource(download.status.localized),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
AnimatedContent(targetState = download.status) { status ->
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
text = if (status in DownloadStatus.running) {
|
||||
"$progress • $speed • $eta"
|
||||
}
|
||||
} else {
|
||||
DateUtils.formatDateTime(
|
||||
LocalContext.current,
|
||||
download.downloadedAt,
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
).toString()
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExpandedMenu()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DownloadListItemPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
DownloadListItem(download = Download.fromApp(app), isExpanded = true)
|
||||
DownloadListItem(download = Download.fromApp(app))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
@@ -6,28 +6,12 @@
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
@@ -35,7 +19,6 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
@@ -47,16 +30,7 @@ import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.room.favourite.Favourite
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Composable to display a favourite app in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param favourite A [Favourite] app to display
|
||||
* @param onClick Callback when this composable is clicked
|
||||
* @param onClear Callback when the favourite button is clicked to remove the app from favourites
|
||||
*/
|
||||
@Composable
|
||||
fun FavouriteListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -64,35 +38,22 @@ fun FavouriteListItem(
|
||||
onClick: () -> Unit = {},
|
||||
onClear: () -> Unit = {}
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
var isVisible by remember { mutableStateOf(true) }
|
||||
|
||||
fun requestClear() {
|
||||
coroutineScope.launch {
|
||||
isVisible = false
|
||||
delay(300) // Let the animation play
|
||||
onClear()
|
||||
}
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isVisible,
|
||||
exit = shrinkVertically() + fadeOut()
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
val context = LocalContext.current
|
||||
RemovableListItem(onRemove = onClear) { triggerRemove ->
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = favourite.displayName,
|
||||
supporting = favourite.packageName,
|
||||
tertiary = DateUtils.formatDateTime(
|
||||
context,
|
||||
favourite.added,
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(modifier = Modifier.weight(1F)) {
|
||||
headlineStyle = MaterialTheme.typography.bodyMedium,
|
||||
onClick = onClick,
|
||||
leading = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
model = ImageRequest.Builder(context)
|
||||
.data(favourite.iconURL)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
@@ -102,40 +63,16 @@ fun FavouriteListItem(
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.margin_small)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = favourite.displayName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = favourite.packageName,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = DateUtils.formatDateTime(
|
||||
LocalContext.current,
|
||||
favourite.added,
|
||||
DateUtils.FORMAT_SHOW_DATE
|
||||
),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { requestClear() }) {
|
||||
},
|
||||
trailing = {
|
||||
IconButton(onClick = triggerRemove) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_favorite_checked),
|
||||
contentDescription = stringResource(R.string.action_favourite)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {}
|
||||
)
|
||||
}
|
||||
@@ -57,10 +57,10 @@ fun Info(
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xxsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small)),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (painter != null) Icon(painter = painter, contentDescription = null)
|
||||
|
||||
@@ -5,35 +5,16 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.installer.SessionInstaller
|
||||
import com.aurora.store.data.model.InstallerInfo
|
||||
|
||||
/**
|
||||
* Composable to display installer details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param installerInfo A [InstallerInfo] object to display details
|
||||
* @param isSelected Whether this installer is selected
|
||||
* @param onClick Callback when this composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun InstallerListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -41,34 +22,14 @@ fun InstallerListItem(
|
||||
isSelected: Boolean = false,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = stringResource(installerInfo.title),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = stringResource(installerInfo.title),
|
||||
supporting = stringResource(installerInfo.subtitle),
|
||||
tertiary = stringResource(installerInfo.description),
|
||||
onClick = onClick,
|
||||
trailing = { RadioButton(selected = isSelected, onClick = onClick) }
|
||||
)
|
||||
Text(
|
||||
text = stringResource(installerInfo.subtitle),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = stringResource(installerInfo.description),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
RadioButton(selected = isSelected, onClick = onClick)
|
||||
}
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
|
||||
@@ -6,21 +6,9 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.VerticalDivider
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -29,7 +17,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import coil3.compose.AsyncImage
|
||||
@@ -38,12 +25,6 @@ import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.model.Link
|
||||
|
||||
/**
|
||||
* Composable to show link details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param link [Link] to show details
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun LinkListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -51,14 +32,12 @@ fun LinkListItem(
|
||||
onClick: () -> Unit = {},
|
||||
iconTint: Color? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
) {
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = link.title,
|
||||
supporting = link.subtitle,
|
||||
onClick = onClick,
|
||||
leading = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(link.icon)
|
||||
@@ -70,27 +49,8 @@ fun LinkListItem(
|
||||
.clip(CircleShape),
|
||||
colorFilter = if (iconTint != null) ColorFilter.tint(color = iconTint) else null
|
||||
)
|
||||
VerticalDivider(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = dimensionResource(R.dimen.margin_large))
|
||||
.width(dimensionResource(R.dimen.padding_xxsmall))
|
||||
.height(dimensionResource(R.dimen.height_header))
|
||||
)
|
||||
Column {
|
||||
Text(
|
||||
text = link.title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = link.subtitle,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
|
||||
@@ -5,32 +5,12 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Composable to display locale details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param displayName Display name of the locale
|
||||
* @param displayLanguage Display name of the language in the locale
|
||||
* @param isChecked Whether the locale is checked/selected
|
||||
* @param onClick Callback when the composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun LocaleListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -39,30 +19,15 @@ fun LocaleListItem(
|
||||
isChecked: Boolean = false,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { if (!isChecked) onClick() })
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = displayName,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = displayLanguage,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = displayName,
|
||||
supporting = displayLanguage,
|
||||
onClick = { if (!isChecked) onClick() },
|
||||
trailing = {
|
||||
Checkbox(checked = isChecked, onCheckedChange = { if (!isChecked) onClick() })
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
|
||||
@@ -36,7 +36,7 @@ fun Logo(modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_small),
|
||||
dimensionResource(R.dimen.spacing_small),
|
||||
Alignment.CenterVertically
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
|
||||
@@ -91,13 +91,13 @@ fun MicroG(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_xxsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_small),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_gms_microg),
|
||||
@@ -116,8 +116,8 @@ fun MicroG(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_large)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_large)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
uiState.items.fastForEach { item ->
|
||||
MicroGItemRow(item = item)
|
||||
@@ -127,7 +127,7 @@ fun MicroG(
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.padding_small)
|
||||
dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
Checkbox(
|
||||
@@ -218,8 +218,8 @@ private fun MicroGItemRow(item: ExternalItem) {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_small),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -232,7 +232,7 @@ private fun MicroGItemRow(item: ExternalItem) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1F)
|
||||
.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Text(
|
||||
text = item.displayName,
|
||||
|
||||
@@ -43,7 +43,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_xsmall),
|
||||
dimensionResource(R.dimen.spacing_xsmall),
|
||||
Alignment.CenterHorizontally
|
||||
)
|
||||
) {
|
||||
@@ -60,7 +60,7 @@ fun PageIndicator(modifier: Modifier = Modifier, totalPages: Int, currentPage: I
|
||||
)
|
||||
val size by animateDpAsState(
|
||||
targetValue = if (isSelected) {
|
||||
dimensionResource(R.dimen.radius_normal)
|
||||
dimensionResource(R.dimen.radius_medium)
|
||||
} else {
|
||||
dimensionResource(R.dimen.radius_small)
|
||||
},
|
||||
|
||||
@@ -133,7 +133,7 @@ fun PermissionList(
|
||||
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
if (header != null) stickyHeader(content = header)
|
||||
|
||||
|
||||
@@ -5,18 +5,10 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
@@ -26,37 +18,17 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.data.model.Permission
|
||||
import com.aurora.store.data.model.PermissionType
|
||||
|
||||
/**
|
||||
* Composable to display permission details in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param permission [Permission] to display
|
||||
* @param onAction Callback when the user clicks the action button
|
||||
*/
|
||||
@Composable
|
||||
fun PermissionListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
permission: Permission,
|
||||
onAction: () -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1F)) {
|
||||
Text(
|
||||
text = permission.title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
text = permission.subtitle,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = permission.title,
|
||||
supporting = permission.subtitle,
|
||||
trailing = {
|
||||
TextButton(onClick = onAction, enabled = !permission.isGranted) {
|
||||
Text(
|
||||
text = if (permission.isGranted) {
|
||||
@@ -69,6 +41,7 @@ fun PermissionListItem(
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Aurora OSS
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
@@ -8,11 +9,11 @@ package com.aurora.store.compose.composable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -29,27 +30,23 @@ import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
/**
|
||||
* Composable to show error message that fills all available screen
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param painter Painter to draw the icon
|
||||
* @param message Message for error
|
||||
* @param actionMessage Message to show on action button; defaults to null with button not visible
|
||||
* @param onAction Callback when action button is clicked
|
||||
* Full-screen empty/error placeholder: centered icon + message and an optional action button.
|
||||
* Replaces the previous EmptyState and Error composables.
|
||||
*/
|
||||
@Composable
|
||||
fun Error(
|
||||
fun Placeholder(
|
||||
modifier: Modifier = Modifier,
|
||||
painter: Painter,
|
||||
message: String,
|
||||
actionMessage: String? = null,
|
||||
actionLabel: String? = null,
|
||||
onAction: (() -> Unit)? = null
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_small),
|
||||
dimensionResource(R.dimen.spacing_small),
|
||||
Alignment.CenterVertically
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
@@ -57,18 +54,18 @@ fun Error(
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size))
|
||||
modifier = Modifier.size(dimensionResource(R.dimen.icon_size_medium)),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
if (actionMessage != null) {
|
||||
Button(onClick = { if (onAction != null) onAction() }, enabled = onAction != null) {
|
||||
if (actionLabel != null && onAction != null) {
|
||||
FilledTonalButton(onClick = onAction) {
|
||||
Text(
|
||||
text = actionMessage,
|
||||
text = actionLabel,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -80,10 +77,11 @@ fun Error(
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun ErrorPreview() {
|
||||
Error(
|
||||
private fun PlaceholderPreview() {
|
||||
Placeholder(
|
||||
painter = painterResource(R.drawable.ic_updates),
|
||||
message = stringResource(R.string.details_no_updates),
|
||||
actionMessage = stringResource(R.string.check_updates)
|
||||
actionLabel = stringResource(R.string.check_updates),
|
||||
onAction = {}
|
||||
)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,24 +5,15 @@
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import coil3.compose.AsyncImage
|
||||
@@ -32,13 +23,6 @@ import com.aurora.gplayapi.SearchSuggestEntry
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
/**
|
||||
* Composable for displaying search suggestions in a list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param searchSuggestEntry A [SearchSuggestEntry] to display search suggestion
|
||||
* @param onClick Callback when this composable is clicked
|
||||
* @param onAction Callback when action button is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun SearchSuggestionListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
@@ -46,25 +30,11 @@ fun SearchSuggestionListItem(
|
||||
onClick: (query: String) -> Unit = {},
|
||||
onAction: (query: String) -> Unit = {}
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = { onClick(searchSuggestEntry.title) })
|
||||
.padding(
|
||||
top = dimensionResource(R.dimen.padding_xsmall),
|
||||
bottom = dimensionResource(R.dimen.padding_xsmall),
|
||||
start = dimensionResource(R.dimen.padding_large),
|
||||
end = dimensionResource(R.dimen.padding_xsmall)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.weight(1F)
|
||||
.clickable(onClick = { onClick(searchSuggestEntry.title) }),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
) {
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = searchSuggestEntry.title,
|
||||
onClick = { onClick(searchSuggestEntry.title) },
|
||||
leading = {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(
|
||||
@@ -81,13 +51,8 @@ fun SearchSuggestionListItem(
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_default))
|
||||
)
|
||||
Text(
|
||||
text = searchSuggestEntry.title,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
},
|
||||
trailing = {
|
||||
IconButton(onClick = { onAction(searchSuggestEntry.title) }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_search_append),
|
||||
@@ -95,6 +60,7 @@ fun SearchSuggestionListItem(
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Aurora OSS
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composable
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
@@ -30,71 +29,33 @@ import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
/**
|
||||
* Section header row with optional right-chevron for navigation (replaces HeaderView).
|
||||
* When [browseUrl] is non-null and non-blank the row is clickable and shows a chevron.
|
||||
* Section header row used throughout the app. Title with optional subtitle, an optional
|
||||
* trailing slot, and the whole row becomes clickable when [onClick] is non-null. When
|
||||
* [trailing] is null and [onClick] is set, a default right-chevron is shown.
|
||||
*/
|
||||
@Composable
|
||||
fun SectionHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
browseUrl: String? = null,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
val hasLink = !browseUrl.isNullOrBlank()
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.then(if (hasLink) Modifier.clickable(onClick = onClick) else Modifier)
|
||||
.padding(all = dimensionResource(R.dimen.padding_medium)),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
if (hasLink) {
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_arrow_right),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(dimensionResource(R.dimen.icon_size_default)),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Section header row with optional subtitle and right-aligned action button
|
||||
* (replaces UpdateHeaderView). Pass `action = null` to omit the button entirely.
|
||||
*/
|
||||
@Composable
|
||||
fun SectionHeaderWithAction(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
action: String? = null,
|
||||
subtitle: String? = null,
|
||||
onAction: () -> Unit = {}
|
||||
onClick: (() -> Unit)? = null,
|
||||
trailing: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.then(if (onClick != null) Modifier.clickable(onClick = onClick) else Modifier)
|
||||
.padding(
|
||||
start = dimensionResource(R.dimen.padding_small),
|
||||
top = dimensionResource(R.dimen.padding_xxsmall),
|
||||
bottom = dimensionResource(R.dimen.padding_xxsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
@@ -108,10 +69,14 @@ fun SectionHeaderWithAction(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!action.isNullOrBlank()) {
|
||||
TextButton(onClick = onAction) {
|
||||
Text(action)
|
||||
}
|
||||
when {
|
||||
trailing != null -> trailing()
|
||||
onClick != null -> Icon(
|
||||
painter = painterResource(R.drawable.ic_arrow_right),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(dimensionResource(R.dimen.icon_size_default)),
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,12 +85,22 @@ fun SectionHeaderWithAction(
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SectionHeaderPreview() {
|
||||
SectionHeader(title = "Top Charts", browseUrl = "https://example.com")
|
||||
SectionHeader(title = "Top Charts", onClick = {})
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SectionHeaderWithSubtitlePreview() {
|
||||
SectionHeader(title = "Permissions", subtitle = "3 requested", onClick = {})
|
||||
}
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun SectionHeaderWithActionPreview() {
|
||||
SectionHeaderWithAction(title = "3 updates available", action = "Update all")
|
||||
SectionHeader(
|
||||
title = "3 updates available",
|
||||
trailing = { TextButton(onClick = {}) { Text("Update all") } }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -66,159 +65,129 @@ fun shimmerBrush(): Brush {
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerCategoryRow() {
|
||||
val brush = shimmerBrush()
|
||||
private fun ShimmerBlock(modifier: Modifier, radiusRes: Int = R.dimen.radius_small) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.clip(RoundedCornerShape(dimensionResource(radiusRes)))
|
||||
.background(shimmerBrush())
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerTextStack(modifier: Modifier = Modifier, widthFractions: List<Float>) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
widthFractions.forEach { fraction ->
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(fraction)
|
||||
.height(14.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ShimmerListRow(showTrailing: Boolean) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_small),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_large))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_category))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
ShimmerBlock(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
)
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(24.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
ShimmerTextStack(
|
||||
modifier = Modifier.weight(1f),
|
||||
widthFractions = listOf(0.7f, 0.5f, 0.5f)
|
||||
)
|
||||
if (showTrailing) {
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.width(80.dp)
|
||||
.height(36.dp),
|
||||
radiusRes = R.dimen.radius_large
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerAppRow() {
|
||||
val brush = shimmerBrush()
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
repeat(3) { i ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (i == 0) 0.75f else 0.5f)
|
||||
.height(14.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
if (i < 2) Spacer(Modifier.height(dimensionResource(R.dimen.padding_xxsmall)))
|
||||
}
|
||||
}
|
||||
}
|
||||
ShimmerListRow(showTrailing = false)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerUpdateItem() {
|
||||
val brush = shimmerBrush()
|
||||
ShimmerListRow(showTrailing = true)
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerCategoryRow() {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
ShimmerBlock(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_category))
|
||||
)
|
||||
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
repeat(3) { i ->
|
||||
Box(
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(if (i == 0) 0.7f else 0.5f)
|
||||
.height(14.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
if (i < 2) Spacer(Modifier.height(dimensionResource(R.dimen.padding_xxsmall)))
|
||||
}
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(80.dp)
|
||||
.height(36.dp)
|
||||
.clip(RoundedCornerShape(50))
|
||||
.background(brush)
|
||||
.weight(1f)
|
||||
.height(24.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerCarouselSection() {
|
||||
val brush = shimmerBrush()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = dimensionResource(R.dimen.padding_small)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
||||
.padding(all = dimensionResource(R.dimen.spacing_small)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Box(
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.42f)
|
||||
.height(16.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
repeat(5) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.padding_xsmall)
|
||||
dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(dimensionResource(R.dimen.icon_size_cluster))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
.background(brush)
|
||||
ShimmerBlock(
|
||||
modifier = Modifier.size(dimensionResource(R.dimen.icon_size_cluster)),
|
||||
radiusRes = R.dimen.radius_medium
|
||||
)
|
||||
Box(
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.width(dimensionResource(R.dimen.icon_size_cluster) * 0.75f)
|
||||
.height(11.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
Box(
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.width(dimensionResource(R.dimen.icon_size_cluster) * 0.5f)
|
||||
.height(11.dp)
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(brush)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -228,13 +197,11 @@ internal fun ShimmerCarouselSection() {
|
||||
|
||||
@Composable
|
||||
internal fun ShimmerAppListItem() {
|
||||
val brush = shimmerBrush()
|
||||
Box(
|
||||
ShimmerBlock(
|
||||
modifier = Modifier
|
||||
.padding(dimensionResource(R.dimen.padding_xsmall))
|
||||
.size(dimensionResource(R.dimen.icon_size_cluster))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
.background(brush)
|
||||
.padding(dimensionResource(R.dimen.spacing_xsmall))
|
||||
.size(dimensionResource(R.dimen.icon_size_cluster)),
|
||||
radiusRes = R.dimen.radius_medium
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
@@ -60,7 +62,7 @@ fun StreamCarousel(
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
state = lazyListState,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(5) { ShimmerCarouselSection() }
|
||||
}
|
||||
@@ -75,10 +77,10 @@ fun StreamCarousel(
|
||||
}
|
||||
|
||||
if (clusters.isEmpty()) {
|
||||
EmptyState(
|
||||
Placeholder(
|
||||
modifier = modifier,
|
||||
icon = R.drawable.ic_apps,
|
||||
message = R.string.no_apps_available
|
||||
painter = painterResource(R.drawable.ic_apps),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
)
|
||||
return
|
||||
}
|
||||
@@ -89,9 +91,9 @@ fun StreamCarousel(
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(
|
||||
if (clusters.size == 1) {
|
||||
R.dimen.margin_medium
|
||||
R.dimen.spacing_medium
|
||||
} else {
|
||||
R.dimen.margin_xxsmall
|
||||
R.dimen.spacing_xsmall
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -109,8 +111,11 @@ fun StreamCarousel(
|
||||
item(key = "header_${cluster.id}") {
|
||||
SectionHeader(
|
||||
title = cluster.clusterTitle,
|
||||
browseUrl = cluster.clusterBrowseUrl,
|
||||
onClick = { onHeaderClick(cluster) }
|
||||
onClick = if (cluster.clusterBrowseUrl.isNotBlank()) {
|
||||
{ onHeaderClick(cluster) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
item(key = "row_${cluster.id}") {
|
||||
@@ -150,8 +155,8 @@ internal fun ClusterRow(
|
||||
|
||||
LazyRow(
|
||||
state = rowState,
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_small)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_small))
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_small)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
itemsIndexed(
|
||||
items = cluster.clusterAppList,
|
||||
|
||||
@@ -30,7 +30,7 @@ fun TextDividerComposable(modifier: Modifier = Modifier, title: String) {
|
||||
Text(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_small)),
|
||||
.padding(dimensionResource(R.dimen.spacing_small)),
|
||||
text = title.uppercase(),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
|
||||
@@ -48,10 +48,10 @@ fun AppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {
|
||||
modifier = modifier
|
||||
.width(dimensionResource(R.dimen.icon_size_cluster))
|
||||
.clickable(onClick = onClick)
|
||||
.padding(all = dimensionResource(R.dimen.padding_xxsmall)),
|
||||
.padding(all = dimensionResource(R.dimen.spacing_xsmall)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_xsmall),
|
||||
dimensionResource(R.dimen.spacing_xsmall),
|
||||
Alignment.CenterVertically
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -55,8 +55,8 @@ fun AppUpdateItem(
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -68,7 +68,7 @@ fun AppUpdateItem(
|
||||
progress = progress
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_medium)))
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.spacing_medium)))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = update.displayName,
|
||||
@@ -91,7 +91,7 @@ fun AppUpdateItem(
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
|
||||
when {
|
||||
onUnignore != null -> {
|
||||
OutlinedButton(onClick = onUnignore) {
|
||||
|
||||
@@ -5,15 +5,9 @@
|
||||
|
||||
package com.aurora.store.compose.composable.app
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -21,7 +15,6 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
@@ -31,50 +24,21 @@ import coil3.request.crossfade
|
||||
import com.aurora.extensions.requiresGMS
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.AuroraListItem
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.util.CommonUtil
|
||||
|
||||
/**
|
||||
* Composable for displaying minimal app details in a vertical-scrollable list
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param app [App] to display
|
||||
* @param onClick Callback when the composable is clicked
|
||||
* @see AppListItem
|
||||
*/
|
||||
@Composable
|
||||
fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Unit = {}) {
|
||||
@Composable
|
||||
fun buildExtras(app: App): List<String> = mutableListOf<String>().apply {
|
||||
add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString)
|
||||
add("${app.labeledRating}★")
|
||||
|
||||
if (app.isFree) {
|
||||
add(stringResource(R.string.details_free))
|
||||
} else {
|
||||
add(stringResource(R.string.details_paid))
|
||||
}
|
||||
|
||||
if (app.containsAds) {
|
||||
add(stringResource(R.string.details_contains_ads))
|
||||
} else {
|
||||
add(stringResource(R.string.details_no_ads))
|
||||
}
|
||||
|
||||
if (app.requiresGMS()) {
|
||||
add(stringResource(R.string.details_gsf_dependent))
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
)
|
||||
) {
|
||||
AuroraListItem(
|
||||
modifier = modifier,
|
||||
headline = app.displayName,
|
||||
supporting = app.developerName,
|
||||
tertiary = buildAppExtras(app),
|
||||
headlineStyle = MaterialTheme.typography.bodyMedium,
|
||||
onClick = onClick,
|
||||
leading = {
|
||||
AsyncImage(
|
||||
modifier = Modifier
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
@@ -86,26 +50,22 @@ fun LargeAppListItem(modifier: Modifier = Modifier, app: App, onClick: () -> Uni
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
) {
|
||||
Text(
|
||||
text = app.displayName,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
@Composable
|
||||
private fun buildAppExtras(app: App): String = buildList {
|
||||
add(if (app.size > 0) CommonUtil.addSiPrefix(app.size) else app.downloadString)
|
||||
add("${app.labeledRating}★")
|
||||
add(stringResource(if (app.isFree) R.string.details_free else R.string.details_paid))
|
||||
add(
|
||||
stringResource(
|
||||
if (app.containsAds) R.string.details_contains_ads else R.string.details_no_ads
|
||||
)
|
||||
Text(text = app.developerName, style = MaterialTheme.typography.bodySmall)
|
||||
Text(
|
||||
text = buildExtras(app).joinToString(separator = " • "),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (app.requiresGMS()) add(stringResource(R.string.details_gsf_dependent))
|
||||
}.joinToString(separator = " • ")
|
||||
|
||||
@PreviewWrapper(ThemePreviewProvider::class)
|
||||
@Preview(showBackground = true)
|
||||
|
||||
@@ -38,7 +38,7 @@ fun ExodusListItem(modifier: Modifier = Modifier, tracker: ExodusTracker) {
|
||||
onClick = { context.browse(tracker.url) },
|
||||
enabled = tracker.url.isNotBlank()
|
||||
)
|
||||
.padding(dimensionResource(R.dimen.padding_small))
|
||||
.padding(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Text(
|
||||
text = tracker.name,
|
||||
|
||||
@@ -34,9 +34,9 @@ fun RatingListItem(modifier: Modifier = Modifier, label: String, rating: Float)
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_small)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_small)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
|
||||
@@ -45,8 +45,8 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) {
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
AsyncImage(
|
||||
@@ -61,7 +61,7 @@ fun ReviewListItem(modifier: Modifier = Modifier, review: Review) {
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Text(
|
||||
text = review.userName,
|
||||
|
||||
@@ -101,7 +101,7 @@ private fun ScreenContent(onAboutAurora: () -> Unit = {}) {
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
@@ -128,9 +128,9 @@ private fun BrandHeader() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.margin_medium)),
|
||||
.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
|
||||
@@ -113,7 +113,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_small)),
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_small)),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
AssistHeader()
|
||||
@@ -121,7 +121,7 @@ private fun ScreenContent(
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
@@ -153,7 +153,9 @@ private fun ScreenContent(
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = dimensionResource(R.dimen.height_bottom_adj)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.account_logout),
|
||||
@@ -166,7 +168,7 @@ private fun ScreenContent(
|
||||
painter = painterResource(R.drawable.ic_logout),
|
||||
contentDescription = null
|
||||
)
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.padding_xsmall)))
|
||||
Spacer(modifier = Modifier.width(dimensionResource(R.dimen.spacing_xsmall)))
|
||||
Text(
|
||||
text = stringResource(R.string.action_logout)
|
||||
)
|
||||
@@ -188,8 +190,8 @@ private fun AssistHeader() {
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal))
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = links.keys.toList(), key = { item -> item }) { label ->
|
||||
AssistChip(
|
||||
|
||||
@@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
@@ -37,7 +38,7 @@ import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.EmptyState
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ShimmerAppRow
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
import com.aurora.store.compose.composition.collectForced
|
||||
@@ -121,7 +122,7 @@ private fun TopChartsBody(
|
||||
) {
|
||||
SecondaryScrollableTabRow(
|
||||
selectedTabIndex = selectedIndex,
|
||||
edgePadding = dimensionResource(R.dimen.padding_small)
|
||||
edgePadding = dimensionResource(R.dimen.spacing_small)
|
||||
) {
|
||||
chartTitles.forEachIndexed { index, titleRes ->
|
||||
Tab(
|
||||
@@ -133,18 +134,18 @@ private fun TopChartsBody(
|
||||
}
|
||||
|
||||
when {
|
||||
state is ViewState.Error -> EmptyState(
|
||||
state is ViewState.Error -> Placeholder(
|
||||
modifier = Modifier.weight(1f),
|
||||
icon = R.drawable.ic_apps,
|
||||
message = R.string.no_apps_available,
|
||||
actionLabel = R.string.action_retry,
|
||||
painter = painterResource(R.drawable.ic_apps),
|
||||
message = stringResource(R.string.no_apps_available),
|
||||
actionLabel = stringResource(R.string.action_retry),
|
||||
onAction = onRetry
|
||||
)
|
||||
|
||||
cluster != null && cluster.clusterAppList.isEmpty() -> EmptyState(
|
||||
cluster != null && cluster.clusterAppList.isEmpty() -> Placeholder(
|
||||
modifier = Modifier.weight(1f),
|
||||
icon = R.drawable.ic_apps,
|
||||
message = R.string.no_apps_available
|
||||
painter = painterResource(R.drawable.ic_apps),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
)
|
||||
|
||||
cluster != null -> {
|
||||
@@ -155,10 +156,10 @@ private fun TopChartsBody(
|
||||
.fillMaxWidth(),
|
||||
state = listState,
|
||||
contentPadding = PaddingValues(
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_xsmall)
|
||||
dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
) {
|
||||
items(count = apps.size, key = { apps[it].id }) { index ->
|
||||
@@ -172,7 +173,7 @@ private fun TopChartsBody(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_large)),
|
||||
.padding(dimensionResource(R.dimen.spacing_large)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
@@ -186,8 +187,10 @@ private fun TopChartsBody(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.padding_small)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xsmall))
|
||||
contentPadding = PaddingValues(vertical = dimensionResource(R.dimen.spacing_small)),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
) {
|
||||
items(8) { ShimmerAppRow() }
|
||||
}
|
||||
|
||||
@@ -248,8 +248,8 @@ private fun ScreenContent(
|
||||
.fillMaxWidth()
|
||||
.clickable { onRequestSearch(pkg.displayName) }
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -298,7 +298,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_xxsmall)
|
||||
dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
) {
|
||||
if (selectedPackages.isNotEmpty()) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import com.aurora.gplayapi.data.models.StreamBundle
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.StreamCarousel
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composition.collectForced
|
||||
@@ -40,7 +40,7 @@ fun CategoryBrowseScreen(
|
||||
topBar = { TopAppBar(title = title) }
|
||||
) { paddingValues ->
|
||||
if (uiState is ViewState.Error) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
|
||||
@@ -22,7 +22,7 @@ import androidx.paging.LoadState
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -54,7 +54,7 @@ fun ExpandedStreamBrowseScreen(
|
||||
is LoadState.Loading -> ContainedLoadingIndicator()
|
||||
|
||||
is LoadState.Error -> {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
@@ -63,7 +63,7 @@ fun ExpandedStreamBrowseScreen(
|
||||
|
||||
else -> {
|
||||
if (apps.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
@@ -74,7 +74,7 @@ fun ExpandedStreamBrowseScreen(
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(
|
||||
|
||||
@@ -93,8 +93,8 @@ fun MoreSheet(
|
||||
|
||||
Column(
|
||||
Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_small),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
mainItems.forEach { item ->
|
||||
@@ -119,8 +119,8 @@ fun MoreSheet(
|
||||
|
||||
Column(
|
||||
Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_small),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_small),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
extraItems.forEach { item ->
|
||||
@@ -153,7 +153,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_large)),
|
||||
.padding(dimensionResource(R.dimen.spacing_large)),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AsyncImage(
|
||||
@@ -171,7 +171,7 @@ private fun AccountHeader(viewModel: MoreViewModel, onNavigateToAccounts: () ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_normal))
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Text(
|
||||
text = if (isAnonymous) {
|
||||
|
||||
@@ -49,8 +49,8 @@ fun NetworkSheet() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_xlarge))
|
||||
.padding(bottom = dimensionResource(R.dimen.padding_large)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_xlarge))
|
||||
.padding(bottom = dimensionResource(R.dimen.spacing_large)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
@@ -58,17 +58,17 @@ fun NetworkSheet() {
|
||||
contentDescription = null,
|
||||
modifier = Modifier.requiredSize(48.dp)
|
||||
)
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_large)))
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_large)))
|
||||
Text(
|
||||
text = stringResource(R.string.title_no_network),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_small)))
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_small)))
|
||||
Text(
|
||||
text = stringResource(R.string.check_connectivity),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
)
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.padding_xlarge)))
|
||||
Spacer(Modifier.padding(top = dimensionResource(R.dimen.spacing_xlarge)))
|
||||
Button(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -73,7 +73,7 @@ private fun ScreenContent(
|
||||
is LoadState.Loading -> ContainedLoadingIndicator()
|
||||
|
||||
is LoadState.Error -> {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
@@ -82,7 +82,7 @@ private fun ScreenContent(
|
||||
|
||||
else -> {
|
||||
if (apps.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
@@ -93,7 +93,7 @@ private fun ScreenContent(
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(
|
||||
|
||||
@@ -66,8 +66,7 @@ import com.aurora.store.ComposeActivity
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ClusterRow
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.ShimmerCarouselSection
|
||||
@@ -212,7 +211,7 @@ private fun ScreenContentError(message: String? = null) {
|
||||
Scaffold(
|
||||
topBar = { TopAppBar() }
|
||||
) { paddingValues ->
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_apps_outage),
|
||||
message = message ?: stringResource(R.string.toast_app_unavailable)
|
||||
@@ -426,7 +425,7 @@ private fun ScreenContentApp(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
),
|
||||
state = listState
|
||||
) {
|
||||
@@ -451,7 +450,7 @@ private fun ScreenContentApp(
|
||||
}
|
||||
|
||||
item {
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_more_about_app),
|
||||
subtitle = app.shortDescription,
|
||||
onClick = { showExtraPane(ExtraScreen.More) }
|
||||
@@ -487,7 +486,7 @@ private fun ScreenContentApp(
|
||||
}
|
||||
|
||||
item {
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_permission),
|
||||
subtitle = if (app.permissions.isNotEmpty()) {
|
||||
stringResource(R.string.permissions_requested, app.permissions.size)
|
||||
|
||||
@@ -40,9 +40,9 @@ import com.aurora.extensions.browse
|
||||
import com.aurora.extensions.isWindowCompact
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.details.ExodusListItem
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
@@ -128,12 +128,12 @@ private fun ScreenContentReport(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
state = listState
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = if (report?.trackers.isNullOrEmpty()) {
|
||||
stringResource(R.string.exodus_no_tracker)
|
||||
} else {
|
||||
@@ -178,11 +178,11 @@ private fun ScreenContentError(
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.failed_to_fetch_report),
|
||||
actionMessage = stringResource(R.string.action_request_analysis),
|
||||
actionLabel = stringResource(R.string.action_request_analysis),
|
||||
onAction = onRequestAnalysis
|
||||
)
|
||||
}
|
||||
|
||||
@@ -139,12 +139,14 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
Info(
|
||||
painter = painterResource(R.drawable.ic_download_manager),
|
||||
@@ -181,7 +183,7 @@ private fun ScreenContent(
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.padding_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
FilledTonalButton(
|
||||
|
||||
@@ -103,10 +103,10 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.navigationBarsPadding()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_small)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_small)),
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.padding_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
FilledTonalButton(
|
||||
@@ -147,7 +147,7 @@ private fun ScreenContent(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
state = listState
|
||||
) {
|
||||
item {
|
||||
|
||||
@@ -38,9 +38,9 @@ import com.aurora.extensions.adaptiveNavigationIcon
|
||||
import com.aurora.extensions.isWindowCompact
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.app.AppListItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -100,16 +100,18 @@ private fun ScreenContent(
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
item {
|
||||
Header(title = stringResource(R.string.details_description))
|
||||
SectionHeader(title = stringResource(R.string.details_description))
|
||||
}
|
||||
|
||||
item {
|
||||
Text(
|
||||
modifier = Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium)
|
||||
),
|
||||
text = AnnotatedString.fromHtml(
|
||||
htmlString = app.description
|
||||
@@ -144,7 +146,7 @@ private fun ScreenContent(
|
||||
*/
|
||||
@Composable
|
||||
private fun AppDependencies(dependencies: List<App>, onNavigateTo: (Destination) -> Unit) {
|
||||
Header(title = stringResource(R.string.details_dependencies))
|
||||
SectionHeader(title = stringResource(R.string.details_dependencies))
|
||||
if (dependencies.isEmpty()) {
|
||||
Info(
|
||||
title = AnnotatedString(text = stringResource(R.string.details_no_dependencies))
|
||||
@@ -166,7 +168,7 @@ private fun AppDependencies(dependencies: List<App>, onNavigateTo: (Destination)
|
||||
*/
|
||||
@Composable
|
||||
private fun AppInfoMore(app: App) {
|
||||
Header(title = stringResource(R.string.details_more_info))
|
||||
SectionHeader(title = stringResource(R.string.details_more_info))
|
||||
Info(
|
||||
title = AnnotatedString(
|
||||
text = stringResource(R.string.details_more_package_name)
|
||||
|
||||
@@ -94,9 +94,11 @@ private fun ScreenContent(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(items = permissionsInfo.keys.toList(), key = { it }) { permission ->
|
||||
val permissionInfo = permissionsInfo.getValue(permission)
|
||||
|
||||
@@ -49,7 +49,7 @@ import com.aurora.extensions.isWindowCompact
|
||||
import com.aurora.gplayapi.data.models.Review
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.details.ReviewListItem
|
||||
@@ -114,7 +114,7 @@ private fun ScreenContent(
|
||||
is LoadState.Loading -> ContainedLoadingIndicator()
|
||||
|
||||
is LoadState.Error -> {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
@@ -167,8 +167,8 @@ private fun FilterHeader(onClick: (filter: Review.Filter) -> Unit) {
|
||||
|
||||
LazyRow(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.margin_normal)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal))
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = filters.keys.toList(), key = { item -> item }) { filter ->
|
||||
val selected = activeFilter == filter
|
||||
|
||||
@@ -53,8 +53,8 @@ fun Actions(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium))),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium))
|
||||
.padding(PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium))),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
val buttonWidthModifier = when {
|
||||
windowAdaptiveInfo.isWindowCompact -> Modifier.weight(1F)
|
||||
@@ -92,7 +92,7 @@ fun Actions(
|
||||
@Composable
|
||||
private fun ActionsPreview() {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Actions(
|
||||
primaryActionDisplayName = stringResource(R.string.action_install),
|
||||
|
||||
@@ -27,7 +27,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
@@ -38,11 +38,11 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
*/
|
||||
@Composable
|
||||
fun Changelog(changelog: String) {
|
||||
Header(title = stringResource(R.string.details_changelog))
|
||||
SectionHeader(title = stringResource(R.string.details_changelog))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_medium))
|
||||
.padding(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -50,8 +50,8 @@ fun Changelog(changelog: String) {
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
|
||||
.background(color = MaterialTheme.colorScheme.secondaryContainer)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
@@ -71,7 +71,7 @@ fun Changelog(changelog: String) {
|
||||
@Composable
|
||||
private fun ChangelogPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Changelog(changelog = app.changes)
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.theme.successColor
|
||||
import com.aurora.store.compose.theme.warningColor
|
||||
@@ -30,7 +30,7 @@ import com.aurora.store.data.model.Scores
|
||||
*/
|
||||
@Composable
|
||||
fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) {
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_compatibility_title),
|
||||
subtitle = stringResource(R.string.plexus_powered)
|
||||
)
|
||||
@@ -82,7 +82,7 @@ fun Compatibility(needsGms: Boolean, plexusScores: Scores? = null) {
|
||||
@Composable
|
||||
private fun CompatibilityPreview() {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Compatibility(needsGms = true)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ import com.aurora.gplayapi.data.models.datasafety.Entry
|
||||
import com.aurora.gplayapi.data.models.datasafety.EntryType
|
||||
import com.aurora.gplayapi.data.models.datasafety.Report
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
fun DataSafety(report: Report, privacyPolicyUrl: String) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_data_safety_title),
|
||||
subtitle = stringResource(R.string.details_data_safety_subtitle),
|
||||
onClick = { context.browse(privacyPolicyUrl) }
|
||||
@@ -86,7 +86,7 @@ fun DataSafety(report: Report, privacyPolicyUrl: String) {
|
||||
@Composable
|
||||
private fun DataSafetyPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
DataSafety(
|
||||
privacyPolicyUrl = app.privacyPolicyUrl,
|
||||
|
||||
@@ -109,7 +109,7 @@ fun Details(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.margin_medium))
|
||||
.padding(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
AnimatedAppIcon(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)),
|
||||
@@ -117,7 +117,7 @@ fun Details(
|
||||
inProgress = state.inProgress(),
|
||||
progress = state.progress()
|
||||
)
|
||||
Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small))) {
|
||||
Column(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_small))) {
|
||||
Text(
|
||||
text = app.displayName,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
@@ -172,7 +172,7 @@ fun Details(
|
||||
@Composable
|
||||
private fun DetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Details(app = app)
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ import com.aurora.extensions.copyToClipBoard
|
||||
import com.aurora.extensions.mailTo
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
@@ -38,7 +38,7 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
fun DeveloperDetails(address: String, website: String, email: String) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Header(title = stringResource(R.string.details_dev_details))
|
||||
SectionHeader(title = stringResource(R.string.details_dev_details))
|
||||
Column {
|
||||
if (website.isNotBlank()) {
|
||||
Info(
|
||||
@@ -74,7 +74,7 @@ fun DeveloperDetails(address: String, website: String, email: String) {
|
||||
@Composable
|
||||
private fun DeveloperDetailsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
DeveloperDetails(
|
||||
address = app.developerAddress,
|
||||
|
||||
@@ -15,8 +15,8 @@ import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.theme.successColor
|
||||
import com.aurora.store.compose.theme.warningColor
|
||||
@@ -30,7 +30,7 @@ import com.aurora.store.data.model.Report
|
||||
*/
|
||||
@Composable
|
||||
fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) {
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_privacy),
|
||||
subtitle = stringResource(R.string.exodus_powered),
|
||||
onClick = onNavigateToDetailsExodus
|
||||
@@ -64,7 +64,7 @@ fun Privacy(report: Report?, onNavigateToDetailsExodus: (() -> Unit)? = null) {
|
||||
@Composable
|
||||
private fun PrivacyPreview() {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Privacy(report = Report(), onNavigateToDetailsExodus = {})
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.Rating
|
||||
import com.aurora.gplayapi.data.models.Review
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.details.RatingListItem
|
||||
import com.aurora.store.compose.composable.details.ReviewListItem
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
@@ -82,7 +82,7 @@ fun RatingAndReviews(
|
||||
}
|
||||
}
|
||||
|
||||
Header(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.details_ratings),
|
||||
onClick = onNavigateToDetailsReview
|
||||
)
|
||||
@@ -90,12 +90,12 @@ fun RatingAndReviews(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_large))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_large))
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
@@ -114,7 +114,7 @@ fun RatingAndReviews(
|
||||
}
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
stars.reversed().fastForEach { star ->
|
||||
RatingListItem(
|
||||
@@ -129,8 +129,8 @@ fun RatingAndReviews(
|
||||
val pagerState = rememberPagerState { featuredReviews.size }
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
pageSpacing = dimensionResource(R.dimen.margin_medium)
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
pageSpacing = dimensionResource(R.dimen.spacing_medium)
|
||||
) { page ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -158,7 +158,7 @@ private fun RatingAndReviewsPreview(@PreviewParameter(AppPreviewProvider::class)
|
||||
)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
RatingAndReviews(rating = app.rating, featuredReviews = reviews)
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
@Composable
|
||||
fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int) -> Unit = {}) {
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
items(items = screenshots, key = { artwork -> artwork.url }) { artwork ->
|
||||
ScreenshotListItem(
|
||||
@@ -57,7 +57,7 @@ fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int)
|
||||
@Composable
|
||||
private fun ScreenshotsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Screenshots(screenshots = app.screenshots)
|
||||
}
|
||||
|
||||
@@ -63,8 +63,8 @@ fun Tags(app: App) {
|
||||
).filterKeys { it != null }
|
||||
|
||||
LazyRow(
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium))
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = tags.keys.toList()) { label ->
|
||||
TagListItem(label = label!!, painter = painterResource(tags.getValue(label)))
|
||||
@@ -77,7 +77,7 @@ fun Tags(app: App) {
|
||||
@Composable
|
||||
private fun TagsPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Tags(app = app)
|
||||
}
|
||||
|
||||
@@ -26,8 +26,8 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewWrapper
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.Header
|
||||
import com.aurora.store.compose.composable.Info
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
|
||||
@@ -40,13 +40,13 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
*/
|
||||
@Composable
|
||||
fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) {
|
||||
Header(title = stringResource(R.string.details_beta))
|
||||
SectionHeader(title = stringResource(R.string.details_beta))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_small))
|
||||
) {
|
||||
Info(
|
||||
modifier = Modifier.weight(1F),
|
||||
@@ -79,7 +79,7 @@ fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Bool
|
||||
@Composable
|
||||
private fun TestingPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Testing(isSubscribed = app.testingProgram!!.isSubscribed)
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import com.aurora.extensions.adaptiveNavigationIcon
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -96,7 +96,7 @@ private fun ScreenContent(
|
||||
is LoadState.Loading -> ContainedLoadingIndicator()
|
||||
|
||||
is LoadState.Error -> {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
@@ -108,9 +108,9 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(count = apps.itemCount, key = apps.itemKey { it.id }) { index ->
|
||||
|
||||
@@ -29,7 +29,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.extensions.copyToClipBoard
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.DispenserListItem
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.viewmodel.dispenser.DispenserViewModel
|
||||
@@ -94,11 +94,11 @@ private fun ScreenContent(
|
||||
}
|
||||
) { paddingValues ->
|
||||
if (dispensers.isEmpty()) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_server),
|
||||
message = stringResource(R.string.no_dispensers_available),
|
||||
actionMessage = stringResource(R.string.add_dispenser_title),
|
||||
actionLabel = stringResource(R.string.add_dispenser_title),
|
||||
onAction = onAddDispenser
|
||||
)
|
||||
} else {
|
||||
@@ -106,7 +106,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = dispensers.toList(), key = { url -> url }) { url ->
|
||||
DispenserListItem(
|
||||
|
||||
@@ -53,7 +53,9 @@ fun InputDispenserDialog(onAdd: (url: String) -> Unit = {}, onDismiss: () -> Uni
|
||||
title = { Text(text = stringResource(R.string.add_dispenser_title)) },
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
Text(text = stringResource(R.string.add_dispenser_summary))
|
||||
OutlinedTextField(
|
||||
|
||||
@@ -41,7 +41,7 @@ import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.DownloadListItem
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -49,6 +49,7 @@ import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.ThemePreviewProvider
|
||||
import com.aurora.store.compose.ui.downloads.menu.DownloadsMenu
|
||||
import com.aurora.store.compose.ui.downloads.menu.MenuItem
|
||||
import com.aurora.store.compose.ui.sheets.DownloadActionsSheet
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.viewmodel.downloads.DownloadsViewModel
|
||||
@@ -114,6 +115,19 @@ private fun ScreenContent(
|
||||
* Save the initial loading state to make sure we don't replay the loading animation again.
|
||||
*/
|
||||
var initialLoad by rememberSaveable { mutableStateOf(true) }
|
||||
var actionsTarget by rememberSaveable { mutableStateOf<Download?>(null) }
|
||||
|
||||
actionsTarget?.let { target ->
|
||||
DownloadActionsSheet(
|
||||
download = target,
|
||||
onDismiss = { actionsTarget = null },
|
||||
onShowDetails = { onNavigateTo(Destination.AppDetails(target.packageName)) },
|
||||
onCancel = { onCancel(target.packageName) },
|
||||
onInstall = { onInstall(target) },
|
||||
onExport = { onExport(target) },
|
||||
onClear = { onClear(target) }
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SetupMenu() {
|
||||
@@ -138,7 +152,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
when {
|
||||
downloads.loadState.refresh is LoadState.Loading && initialLoad -> {
|
||||
@@ -149,7 +163,7 @@ private fun ScreenContent(
|
||||
initialLoad = false
|
||||
|
||||
if (downloads.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_download_manager),
|
||||
message = stringResource(R.string.download_none)
|
||||
@@ -168,15 +182,7 @@ private fun ScreenContent(
|
||||
DownloadListItem(
|
||||
modifier = Modifier.animateItem(),
|
||||
download = download,
|
||||
onClick = {
|
||||
onNavigateTo(
|
||||
Destination.AppDetails(download.packageName)
|
||||
)
|
||||
},
|
||||
onClear = { onClear(download) },
|
||||
onCancel = { onCancel(download.packageName) },
|
||||
onExport = { onExport(download) },
|
||||
onInstall = { onInstall(download) }
|
||||
onClick = { actionsTarget = download }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ import com.aurora.extensions.emptyPagingItems
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.FavouriteListItem
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -140,7 +140,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
when {
|
||||
favourites.loadState.refresh is LoadState.Loading && initialLoad -> {
|
||||
@@ -151,7 +151,7 @@ private fun ScreenContent(
|
||||
initialLoad = false
|
||||
|
||||
if (favourites.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_favorite_unchecked),
|
||||
message = stringResource(R.string.details_no_favourites)
|
||||
|
||||
@@ -37,7 +37,7 @@ import com.aurora.extensions.emptyPagingItems
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.TopAppBar
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
@@ -95,7 +95,7 @@ private fun ScreenContent(
|
||||
initialLoad = false
|
||||
|
||||
if (apps.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_apps_outage),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
@@ -106,7 +106,7 @@ private fun ScreenContent(
|
||||
LazyColumn(
|
||||
state = listState,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(
|
||||
|
||||
@@ -61,12 +61,12 @@ private fun ScreenContent(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
state = listState
|
||||
) {
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_title_gsf),
|
||||
|
||||
@@ -99,16 +99,16 @@ private fun ScreenContent(
|
||||
.fillMaxWidth()
|
||||
.navigationBarsPadding()
|
||||
.padding(
|
||||
vertical = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_medium),
|
||||
horizontal = if (windowAdaptiveInfo.isWindowCompact) {
|
||||
dimensionResource(R.dimen.padding_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
} else {
|
||||
dimensionResource(R.dimen.padding_xlarge)
|
||||
dimensionResource(R.dimen.spacing_xlarge)
|
||||
}
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.padding_medium),
|
||||
dimensionResource(R.dimen.spacing_medium),
|
||||
Alignment.End
|
||||
)
|
||||
) {
|
||||
|
||||
@@ -45,12 +45,12 @@ private fun PageContent(
|
||||
onPermissionCallback: (type: PermissionType) -> Unit = {}
|
||||
) {
|
||||
PermissionList(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
permissions = permissions,
|
||||
onPermissionCallback = onPermissionCallback,
|
||||
header = {
|
||||
Column(
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_title_permissions),
|
||||
|
||||
@@ -96,12 +96,12 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) {
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Column(
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
|
||||
modifier = Modifier.padding(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.onboarding_title_welcome),
|
||||
|
||||
@@ -76,7 +76,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = availableInstallers, key = { i -> i.id }) { installerInfo ->
|
||||
InstallerListItem(
|
||||
|
||||
@@ -228,7 +228,7 @@ private fun ProxyURLDialog(
|
||||
Column {
|
||||
Text(
|
||||
text = stringResource(R.string.pref_network_proxy_url_message),
|
||||
modifier = Modifier.padding(bottom = dimensionResource(R.dimen.padding_small))
|
||||
modifier = Modifier.padding(bottom = dimensionResource(R.dimen.spacing_small))
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = url,
|
||||
@@ -255,7 +255,7 @@ private fun ProxyURLDialog(
|
||||
Text(stringResource(R.string.disable))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(android.R.string.cancel))
|
||||
}
|
||||
@@ -286,14 +286,14 @@ internal fun SingleChoiceDialog(
|
||||
onClick = { onSelect(index) },
|
||||
role = Role.RadioButton
|
||||
)
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_small)),
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_small)),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = index == selected,
|
||||
onClick = null
|
||||
)
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.margin_small)))
|
||||
Spacer(Modifier.width(dimensionResource(R.dimen.spacing_small)))
|
||||
Text(option)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ private fun UpdatesRestrictionsDialog(onUpdateAutomatedCheck: () -> Unit, onDism
|
||||
text = {
|
||||
Column {
|
||||
Text(stringResource(R.string.pref_updates_restrictions_desc))
|
||||
Spacer(Modifier.height(dimensionResource(R.dimen.margin_small)))
|
||||
Spacer(Modifier.height(dimensionResource(R.dimen.spacing_small)))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
|
||||
@@ -74,7 +74,7 @@ import com.aurora.gplayapi.SearchSuggestEntry
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.ContainedLoadingIndicator
|
||||
import com.aurora.store.compose.composable.Error
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.ScrollHint
|
||||
import com.aurora.store.compose.composable.SearchSuggestionListItem
|
||||
import com.aurora.store.compose.composable.app.LargeAppListItem
|
||||
@@ -238,7 +238,7 @@ private fun ScreenContent(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
.padding(vertical = dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
FilterHeader(
|
||||
isEnabled = isSearching && results.loadState.refresh is LoadState.NotLoading,
|
||||
@@ -250,7 +250,7 @@ private fun ScreenContent(
|
||||
is LoadState.Loading -> ContainedLoadingIndicator()
|
||||
|
||||
is LoadState.Error -> {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.error)
|
||||
@@ -259,7 +259,7 @@ private fun ScreenContent(
|
||||
|
||||
else -> {
|
||||
if (isSearching && results.itemCount == 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
painter = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.no_apps_available)
|
||||
@@ -271,7 +271,7 @@ private fun ScreenContent(
|
||||
state = listState,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(
|
||||
@@ -316,7 +316,7 @@ private fun ScreenContent(
|
||||
|
||||
else -> {
|
||||
if (isSearching && results.itemCount > 0) {
|
||||
Error(
|
||||
Placeholder(
|
||||
painter = painterResource(R.drawable.ic_round_search),
|
||||
message = stringResource(R.string.select_app_for_details)
|
||||
)
|
||||
@@ -435,8 +435,8 @@ private fun FilterHeader(
|
||||
LazyRow(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal))
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_medium)),
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
items(items = filters, key = { item -> item }) { filter ->
|
||||
val isSelected = when (filter) {
|
||||
|
||||
@@ -82,7 +82,7 @@ fun AppUpdateSheet(
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -107,7 +107,7 @@ fun AppUpdateSheet(
|
||||
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -159,11 +159,11 @@ private fun AppHeader(update: Update, onShowDetails: () -> Unit) {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
|
||||
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_medium))
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
@@ -212,12 +212,12 @@ private fun Item(label: String, onClick: () -> Unit) {
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_xsmall))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_normal)))
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_xsmall))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -228,8 +228,8 @@ private fun ChangelogSection(html: String) {
|
||||
text = stringResource(R.string.details_changelog),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -238,8 +238,8 @@ private fun ChangelogSection(html: String) {
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
horizontal = dimensionResource(R.dimen.spacing_medium),
|
||||
vertical = dimensionResource(R.dimen.spacing_xsmall)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -227,7 +227,7 @@ fun SplashScreen(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(0.1f)
|
||||
.padding(horizontal = dimensionResource(R.dimen.padding_xlarge)),
|
||||
.padding(horizontal = dimensionResource(R.dimen.spacing_xlarge)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
val (statusText, statusColor) = when {
|
||||
@@ -260,7 +260,7 @@ fun SplashScreen(
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_small)
|
||||
dimensionResource(R.dimen.spacing_small)
|
||||
)
|
||||
) {
|
||||
Button(
|
||||
|
||||
@@ -53,7 +53,7 @@ private fun PageContent(
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
@@ -52,7 +52,7 @@ private fun PageContent(
|
||||
) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
|
||||
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.spacing_xsmall))
|
||||
) {
|
||||
stickyHeader {
|
||||
Surface(modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
@@ -17,12 +19,13 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composable.EmptyState
|
||||
import com.aurora.store.compose.composable.SectionHeaderWithAction
|
||||
import com.aurora.store.compose.composable.Placeholder
|
||||
import com.aurora.store.compose.composable.SectionHeader
|
||||
import com.aurora.store.compose.composable.ShimmerUpdateItem
|
||||
import com.aurora.store.compose.composable.app.AppUpdateItem
|
||||
import com.aurora.store.compose.navigation.Destination
|
||||
@@ -82,7 +85,7 @@ fun UpdatesScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
items(10) { ShimmerUpdateItem() }
|
||||
@@ -90,11 +93,11 @@ fun UpdatesScreen(
|
||||
}
|
||||
|
||||
updateMap.isEmpty() -> {
|
||||
EmptyState(
|
||||
Placeholder(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
icon = R.drawable.ic_updates,
|
||||
message = R.string.details_no_updates,
|
||||
actionLabel = R.string.check_updates,
|
||||
painter = painterResource(R.drawable.ic_updates),
|
||||
message = stringResource(R.string.details_no_updates),
|
||||
actionLabel = stringResource(R.string.check_updates),
|
||||
onAction = { viewModel.fetchUpdates() }
|
||||
)
|
||||
}
|
||||
@@ -103,7 +106,7 @@ fun UpdatesScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
dimensionResource(R.dimen.margin_medium)
|
||||
dimensionResource(R.dimen.spacing_medium)
|
||||
)
|
||||
) {
|
||||
if (mainEntries.isNotEmpty()) {
|
||||
@@ -122,16 +125,19 @@ fun UpdatesScreen(
|
||||
R.string.action_update_all
|
||||
}
|
||||
)
|
||||
SectionHeaderWithAction(
|
||||
SectionHeader(
|
||||
title = title,
|
||||
action = actionLabel,
|
||||
onAction = {
|
||||
trailing = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
if (mainAllEnqueued) {
|
||||
onCancelAll()
|
||||
} else {
|
||||
onRequestUpdateAll(mainEntries.map { it.key })
|
||||
}
|
||||
}
|
||||
) { Text(actionLabel) }
|
||||
}
|
||||
)
|
||||
}
|
||||
updateItems(
|
||||
@@ -145,13 +151,16 @@ fun UpdatesScreen(
|
||||
|
||||
if (approvalEntries.isNotEmpty()) {
|
||||
item(key = "header_approval") {
|
||||
SectionHeaderWithAction(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.updates_approval_header),
|
||||
subtitle = stringResource(R.string.updates_approval_desc),
|
||||
action = stringResource(R.string.action_update_all),
|
||||
onAction = {
|
||||
trailing = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onRequestUpdateAll(approvalEntries.map { it.key })
|
||||
}
|
||||
) { Text(stringResource(R.string.action_update_all)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
updateItems(
|
||||
@@ -165,7 +174,7 @@ fun UpdatesScreen(
|
||||
|
||||
if (incompatibleEntries.isNotEmpty()) {
|
||||
item(key = "header_incompatible") {
|
||||
SectionHeaderWithAction(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.updates_incompatible_header),
|
||||
subtitle = stringResource(R.string.updates_incompatible_desc)
|
||||
)
|
||||
@@ -181,7 +190,7 @@ fun UpdatesScreen(
|
||||
|
||||
if (ignoredUpdates.isNotEmpty()) {
|
||||
item(key = "header_ignored") {
|
||||
SectionHeaderWithAction(
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.updates_ignored_header),
|
||||
subtitle = stringResource(R.string.updates_ignored_desc)
|
||||
)
|
||||
|
||||
@@ -22,9 +22,9 @@
|
||||
<corners android:radius="8dp" />
|
||||
<solid android:color="?colorControlHighlight" />
|
||||
<padding
|
||||
android:bottom="@dimen/margin_medium"
|
||||
android:left="@dimen/margin_medium"
|
||||
android:right="@dimen/margin_medium"
|
||||
android:top="@dimen/margin_medium" />
|
||||
android:bottom="@dimen/spacing_medium"
|
||||
android:left="@dimen/spacing_medium"
|
||||
android:right="@dimen/spacing_medium"
|
||||
android:top="@dimen/spacing_medium" />
|
||||
</shape>
|
||||
|
||||
|
||||
@@ -21,6 +21,6 @@
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@android:color/transparent" />
|
||||
<size
|
||||
android:width="@dimen/margin_small"
|
||||
android:height="@dimen/margin_small" />
|
||||
android:width="@dimen/spacing_small"
|
||||
android:height="@dimen/spacing_small" />
|
||||
</shape>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<solid android:color="?colorAccent" />
|
||||
<padding
|
||||
android:left="0dp"
|
||||
android:right="@dimen/padding_xlarge" />
|
||||
android:right="@dimen/spacing_xlarge" />
|
||||
<corners android:radius="5dp" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -18,21 +18,11 @@
|
||||
-->
|
||||
|
||||
<resources>
|
||||
<dimen name="padding_xxsmall">2dp</dimen>
|
||||
<dimen name="padding_xsmall">4dp</dimen>
|
||||
<dimen name="padding_small">8dp</dimen>
|
||||
<dimen name="padding_medium">10dp</dimen>
|
||||
<dimen name="padding_normal">12dp</dimen>
|
||||
<dimen name="padding_large">16dp</dimen>
|
||||
<dimen name="padding_xlarge">32dp</dimen>
|
||||
|
||||
<dimen name="margin_xxsmall">2dp</dimen>
|
||||
<dimen name="margin_xsmall">4dp</dimen>
|
||||
<dimen name="margin_small">8dp</dimen>
|
||||
<dimen name="margin_medium">10dp</dimen>
|
||||
<dimen name="margin_normal">12dp</dimen>
|
||||
<dimen name="margin_large">16dp</dimen>
|
||||
<dimen name="margin_xlarge">32dp</dimen>
|
||||
<dimen name="spacing_xsmall">4dp</dimen>
|
||||
<dimen name="spacing_small">8dp</dimen>
|
||||
<dimen name="spacing_medium">12dp</dimen>
|
||||
<dimen name="spacing_large">16dp</dimen>
|
||||
<dimen name="spacing_xlarge">24dp</dimen>
|
||||
|
||||
<dimen name="icon_size_default">24dp</dimen>
|
||||
<dimen name="icon_size_category">36dp</dimen>
|
||||
@@ -69,8 +59,7 @@
|
||||
<dimen name="review_height">200dp</dimen>
|
||||
|
||||
<dimen name="radius_small">8dp</dimen>
|
||||
<dimen name="radius_medium">10dp</dimen>
|
||||
<dimen name="radius_normal">12dp</dimen>
|
||||
<dimen name="radius_medium">12dp</dimen>
|
||||
<dimen name="radius_large">16dp</dimen>
|
||||
|
||||
<dimen name="scroll_hint_padding">5dp</dimen>
|
||||
|
||||
Reference in New Issue
Block a user