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