mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
feat(downloads): update downloaded item handling
This commit is contained in:
@@ -24,8 +24,8 @@ import androidx.compose.ui.platform.testTag
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import hu.bbara.purefin.ui.common.card.PosterCard
|
|
||||||
import hu.bbara.purefin.core.feature.downloads.DownloadsViewModel
|
import hu.bbara.purefin.core.feature.downloads.DownloadsViewModel
|
||||||
|
import hu.bbara.purefin.ui.common.card.PosterCard
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DownloadsContent(
|
fun DownloadsContent(
|
||||||
@@ -101,14 +101,17 @@ fun DownloadsContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items(downloads.value, key = { item -> item.id }) { item ->
|
items(downloads.value, key = { item -> item.media.id }) { item ->
|
||||||
PosterCard(
|
Column(
|
||||||
item = item,
|
modifier = Modifier.testTag("$DownloadsItemTagPrefix${item.media.id}")
|
||||||
onMovieSelected = viewModel::onMovieSelected,
|
) {
|
||||||
onSeriesSelected = viewModel::onSeriesSelected,
|
PosterCard(
|
||||||
onEpisodeSelected = { _, _, _ -> },
|
item = item.media,
|
||||||
modifier = Modifier.testTag("$DownloadsItemTagPrefix${item.id}")
|
onMovieSelected = viewModel::onMovieSelected,
|
||||||
)
|
onSeriesSelected = viewModel::onSeriesSelected,
|
||||||
|
onEpisodeSelected = { _, _, _ -> },
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
package hu.bbara.purefin.ui.screen.series
|
package hu.bbara.purefin.ui.screen.series
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
@@ -50,30 +54,94 @@ fun SeriesScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val seriesState = viewModel.series.collectAsStateWithLifecycle()
|
val seriesState = viewModel.series.collectAsStateWithLifecycle()
|
||||||
|
var pendingDownloadOption by remember { mutableStateOf<Pair<SeriesDownloadOption, Season>?>(null) }
|
||||||
|
|
||||||
|
val notificationPermissionLauncher = rememberLauncherForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { _ ->
|
||||||
|
// Proceed with download regardless — notification is nice-to-have
|
||||||
|
val pendingOption = pendingDownloadOption
|
||||||
|
pendingDownloadOption = null
|
||||||
|
val (option, selectedSeason) = pendingOption ?: return@rememberLauncherForActivityResult
|
||||||
|
val seriesData = seriesState.value ?: return@rememberLauncherForActivityResult
|
||||||
|
val canPerformOption = when (option) {
|
||||||
|
SeriesDownloadOption.SEASON -> viewModel.seasonDownloadState.value is DownloadState.NotDownloaded
|
||||||
|
SeriesDownloadOption.SERIES -> viewModel.seriesDownloadState.value is DownloadState.NotDownloaded
|
||||||
|
SeriesDownloadOption.SMART -> !viewModel.isSmartDownloadEnabled.value
|
||||||
|
SeriesDownloadOption.DELETE_SMART -> true
|
||||||
|
}
|
||||||
|
if (!canPerformOption) return@rememberLauncherForActivityResult
|
||||||
|
when (option) {
|
||||||
|
SeriesDownloadOption.SEASON ->
|
||||||
|
viewModel.downloadSeason(seriesData.id, selectedSeason.id)
|
||||||
|
|
||||||
|
SeriesDownloadOption.SERIES ->
|
||||||
|
viewModel.downloadSeries(seriesData)
|
||||||
|
|
||||||
|
SeriesDownloadOption.SMART ->
|
||||||
|
viewModel.enableSmartDownload(seriesData.id)
|
||||||
|
|
||||||
|
SeriesDownloadOption.DELETE_SMART ->
|
||||||
|
viewModel.deleteSmartDownloads(seriesData.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val seriesData = seriesState.value
|
val seriesData = seriesState.value
|
||||||
if (seriesData != null && seriesData.seasons.isNotEmpty()) {
|
if (seriesData != null && seriesData.seasons.isNotEmpty()) {
|
||||||
LaunchedEffect(seriesData) {
|
LaunchedEffect(seriesData) {
|
||||||
viewModel.observeSeriesDownloadState(seriesData)
|
viewModel.observeSeriesDownloadState(seriesData)
|
||||||
}
|
}
|
||||||
|
val seriesDownloadState = viewModel.seriesDownloadState.collectAsStateWithLifecycle().value
|
||||||
|
val seasonDownloadState = viewModel.seasonDownloadState.collectAsStateWithLifecycle().value
|
||||||
|
val isSmartDownloadEnabled = viewModel.isSmartDownloadEnabled.collectAsStateWithLifecycle().value
|
||||||
|
|
||||||
|
fun canPerformDownloadOption(option: SeriesDownloadOption): Boolean = when (option) {
|
||||||
|
SeriesDownloadOption.SEASON -> seasonDownloadState is DownloadState.NotDownloaded
|
||||||
|
SeriesDownloadOption.SERIES -> seriesDownloadState is DownloadState.NotDownloaded
|
||||||
|
SeriesDownloadOption.SMART -> !isSmartDownloadEnabled
|
||||||
|
SeriesDownloadOption.DELETE_SMART -> true
|
||||||
|
}
|
||||||
|
|
||||||
|
fun performDownloadOption(option: SeriesDownloadOption, selectedSeason: Season) {
|
||||||
|
if (!canPerformDownloadOption(option)) return
|
||||||
|
when (option) {
|
||||||
|
SeriesDownloadOption.SEASON ->
|
||||||
|
viewModel.downloadSeason(seriesData.id, selectedSeason.id)
|
||||||
|
|
||||||
|
SeriesDownloadOption.SERIES ->
|
||||||
|
viewModel.downloadSeries(seriesData)
|
||||||
|
|
||||||
|
SeriesDownloadOption.SMART ->
|
||||||
|
viewModel.enableSmartDownload(seriesData.id)
|
||||||
|
|
||||||
|
SeriesDownloadOption.DELETE_SMART ->
|
||||||
|
viewModel.deleteSmartDownloads(seriesData.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun shouldRequestNotificationPermission(option: SeriesDownloadOption): Boolean {
|
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return false
|
||||||
|
return when (option) {
|
||||||
|
SeriesDownloadOption.SEASON,
|
||||||
|
SeriesDownloadOption.SERIES,
|
||||||
|
SeriesDownloadOption.SMART -> canPerformDownloadOption(option)
|
||||||
|
SeriesDownloadOption.DELETE_SMART -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SeriesScreenInternal(
|
SeriesScreenInternal(
|
||||||
series = seriesData,
|
series = seriesData,
|
||||||
seriesDownloadState = viewModel.seriesDownloadState.collectAsStateWithLifecycle().value,
|
seriesDownloadState = seriesDownloadState,
|
||||||
seasonDownloadState = viewModel.seasonDownloadState.collectAsStateWithLifecycle().value,
|
seasonDownloadState = seasonDownloadState,
|
||||||
isSmartDownloadEnabled = viewModel.isSmartDownloadEnabled.collectAsStateWithLifecycle().value,
|
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
||||||
onDownloadOptionSelected = { option, selectedSeason ->
|
onDownloadOptionSelected = { option, selectedSeason ->
|
||||||
when (option) {
|
if (canPerformDownloadOption(option)) {
|
||||||
SeriesDownloadOption.SEASON ->
|
if (shouldRequestNotificationPermission(option)) {
|
||||||
viewModel.downloadSeason(seriesData.id, selectedSeason.id)
|
pendingDownloadOption = option to selectedSeason
|
||||||
|
notificationPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
|
||||||
SeriesDownloadOption.SERIES ->
|
} else {
|
||||||
viewModel.downloadSeries(seriesData)
|
performDownloadOption(option, selectedSeason)
|
||||||
|
}
|
||||||
SeriesDownloadOption.SMART ->
|
|
||||||
viewModel.enableSmartDownload(seriesData.id)
|
|
||||||
|
|
||||||
SeriesDownloadOption.DELETE_SMART ->
|
|
||||||
viewModel.deleteSmartDownloads(seriesData.id)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
||||||
@@ -132,8 +200,8 @@ private fun SeriesScreenInternal(
|
|||||||
) {
|
) {
|
||||||
SeriesActionButtons(
|
SeriesActionButtons(
|
||||||
nextUpEpisode = nextUpEpisode,
|
nextUpEpisode = nextUpEpisode,
|
||||||
seriesDownloadState = seriesDownloadState,
|
|
||||||
selectedSeason = selectedSeason,
|
selectedSeason = selectedSeason,
|
||||||
|
seriesDownloadState = seriesDownloadState,
|
||||||
seasonDownloadState = seasonDownloadState,
|
seasonDownloadState = seasonDownloadState,
|
||||||
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
||||||
offline = offline,
|
offline = offline,
|
||||||
@@ -192,7 +260,7 @@ private fun SeriesScreenPreview() {
|
|||||||
AppTheme {
|
AppTheme {
|
||||||
SeriesScreenInternal(
|
SeriesScreenInternal(
|
||||||
series = previewSeries(),
|
series = previewSeries(),
|
||||||
seriesDownloadState = DownloadState.Downloading(progressPercent = 0.58f),
|
seriesDownloadState = DownloadState.NotDownloaded,
|
||||||
seasonDownloadState = DownloadState.NotDownloaded,
|
seasonDownloadState = DownloadState.NotDownloaded,
|
||||||
isSmartDownloadEnabled = true,
|
isSmartDownloadEnabled = true,
|
||||||
onDownloadOptionSelected = { _, _ -> },
|
onDownloadOptionSelected = { _, _ -> },
|
||||||
|
|||||||
@@ -26,18 +26,19 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Add
|
import androidx.compose.material.icons.outlined.Add
|
||||||
import androidx.compose.material.icons.outlined.ArrowBack
|
import androidx.compose.material.icons.outlined.ArrowBack
|
||||||
|
import androidx.compose.material.icons.outlined.AutoAwesome
|
||||||
import androidx.compose.material.icons.outlined.Cast
|
import androidx.compose.material.icons.outlined.Cast
|
||||||
import androidx.compose.material.icons.outlined.Close
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.Download
|
import androidx.compose.material.icons.outlined.Download
|
||||||
import androidx.compose.material.icons.outlined.DownloadDone
|
|
||||||
import androidx.compose.material.icons.outlined.MoreVert
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
import androidx.compose.material.icons.outlined.PlayCircle
|
import androidx.compose.material.icons.outlined.PlayCircle
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.ListItem
|
||||||
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -48,6 +49,7 @@ 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
|
||||||
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
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.platform.testTag
|
import androidx.compose.ui.platform.testTag
|
||||||
@@ -111,8 +113,8 @@ internal fun SeriesMetaChips(series: Series) {
|
|||||||
@Composable
|
@Composable
|
||||||
internal fun SeriesActionButtons(
|
internal fun SeriesActionButtons(
|
||||||
nextUpEpisode: Episode?,
|
nextUpEpisode: Episode?,
|
||||||
seriesDownloadState: DownloadState,
|
|
||||||
selectedSeason: Season,
|
selectedSeason: Season,
|
||||||
|
seriesDownloadState: DownloadState,
|
||||||
seasonDownloadState: DownloadState,
|
seasonDownloadState: DownloadState,
|
||||||
isSmartDownloadEnabled: Boolean,
|
isSmartDownloadEnabled: Boolean,
|
||||||
offline: Boolean,
|
offline: Boolean,
|
||||||
@@ -121,7 +123,6 @@ internal fun SeriesActionButtons(
|
|||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val navigationManager = LocalNavigationManager.current
|
val navigationManager = LocalNavigationManager.current
|
||||||
val scheme = MaterialTheme.colorScheme
|
|
||||||
var showDownloadDialog by remember { mutableStateOf(false) }
|
var showDownloadDialog by remember { mutableStateOf(false) }
|
||||||
val playAction = remember(nextUpEpisode, offline) {
|
val playAction = remember(nextUpEpisode, offline) {
|
||||||
nextUpEpisode?.let { episode ->
|
nextUpEpisode?.let { episode ->
|
||||||
@@ -163,11 +164,7 @@ internal fun SeriesActionButtons(
|
|||||||
MediaActionButton(
|
MediaActionButton(
|
||||||
backgroundColor = MaterialTheme.colorScheme.surface,
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
iconColor = MaterialTheme.colorScheme.onSurface,
|
iconColor = MaterialTheme.colorScheme.onSurface,
|
||||||
icon = when {
|
icon = Icons.Outlined.Download,
|
||||||
seriesDownloadState is DownloadState.Downloading -> Icons.Outlined.Close
|
|
||||||
seriesDownloadState is DownloadState.Downloaded -> Icons.Outlined.DownloadDone
|
|
||||||
else -> Icons.Outlined.Download
|
|
||||||
},
|
|
||||||
height = 48.dp,
|
height = 48.dp,
|
||||||
modifier = Modifier.testTag(SeriesDownloadButtonTag),
|
modifier = Modifier.testTag(SeriesDownloadButtonTag),
|
||||||
onClick = { showDownloadDialog = true }
|
onClick = { showDownloadDialog = true }
|
||||||
@@ -175,8 +172,9 @@ internal fun SeriesActionButtons(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showDownloadDialog) {
|
if (showDownloadDialog) {
|
||||||
DownloadOptionsDialog(
|
DownloadOptionsBottomSheet(
|
||||||
selectedSeasonName = selectedSeason.name,
|
selectedSeasonName = selectedSeason.name,
|
||||||
|
seriesDownloadState = seriesDownloadState,
|
||||||
seasonDownloadState = seasonDownloadState,
|
seasonDownloadState = seasonDownloadState,
|
||||||
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
isSmartDownloadEnabled = isSmartDownloadEnabled,
|
||||||
onDownloadOptionSelected = {
|
onDownloadOptionSelected = {
|
||||||
@@ -195,68 +193,136 @@ internal enum class SeriesDownloadOption {
|
|||||||
DELETE_SMART
|
DELETE_SMART
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun DownloadOptionsDialog(
|
private fun DownloadOptionsBottomSheet(
|
||||||
selectedSeasonName: String,
|
selectedSeasonName: String,
|
||||||
|
seriesDownloadState: DownloadState,
|
||||||
seasonDownloadState: DownloadState,
|
seasonDownloadState: DownloadState,
|
||||||
isSmartDownloadEnabled: Boolean,
|
isSmartDownloadEnabled: Boolean,
|
||||||
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
onDownloadOptionSelected: (SeriesDownloadOption) -> Unit,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
) {
|
) {
|
||||||
AlertDialog(
|
ModalBottomSheet(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
modifier = Modifier.testTag(SeriesDownloadDialogTag),
|
modifier = Modifier.testTag(SeriesDownloadDialogTag),
|
||||||
title = { Text("Download") },
|
) {
|
||||||
text = {
|
Column(
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 24.dp)
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "Download options",
|
||||||
|
style = MaterialTheme.typography.titleLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
Text(
|
Text(
|
||||||
text = "Choose how to download this series.",
|
text = "Choose how to download this series.",
|
||||||
style = MaterialTheme.typography.bodyMedium
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
|
||||||
confirmButton = {
|
DownloadOptionRow(
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
title = "Download selected season",
|
||||||
TextButton(
|
supportingText = when (seasonDownloadState) {
|
||||||
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SEASON) },
|
is DownloadState.Downloaded -> "$selectedSeasonName is already downloaded."
|
||||||
modifier = Modifier.testTag(SeriesDownloadSeasonButtonTag)
|
is DownloadState.Downloading -> "$selectedSeasonName is downloading."
|
||||||
) {
|
else -> "Save episodes from $selectedSeasonName for offline viewing."
|
||||||
Text(
|
},
|
||||||
when (seasonDownloadState) {
|
icon = Icons.Outlined.Download,
|
||||||
is DownloadState.Downloaded -> "$selectedSeasonName Downloaded"
|
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SEASON) },
|
||||||
is DownloadState.Downloading -> "Downloading $selectedSeasonName"
|
enabled = seasonDownloadState is DownloadState.NotDownloaded,
|
||||||
else -> "Download $selectedSeasonName"
|
modifier = Modifier.testTag(SeriesDownloadSeasonButtonTag)
|
||||||
}
|
)
|
||||||
)
|
DownloadOptionRow(
|
||||||
}
|
title = "Download all episodes",
|
||||||
TextButton(
|
supportingText = when (seriesDownloadState) {
|
||||||
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SERIES) },
|
is DownloadState.Downloaded -> "All episodes in this series are already downloaded."
|
||||||
modifier = Modifier.testTag(SeriesDownloadAllButtonTag)
|
is DownloadState.Downloading -> "All episodes in this series are downloading."
|
||||||
) {
|
else -> "Save every available episode in this series."
|
||||||
Text("Download All")
|
},
|
||||||
}
|
icon = Icons.Outlined.Download,
|
||||||
}
|
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SERIES) },
|
||||||
},
|
enabled = seriesDownloadState is DownloadState.NotDownloaded,
|
||||||
dismissButton = {
|
modifier = Modifier.testTag(SeriesDownloadAllButtonTag)
|
||||||
|
)
|
||||||
if (isSmartDownloadEnabled) {
|
if (isSmartDownloadEnabled) {
|
||||||
TextButton(
|
DownloadOptionRow(
|
||||||
|
title = "Delete series downloads",
|
||||||
|
supportingText = "Turns off Smart Downloads and removes offline episodes for this series.",
|
||||||
|
icon = Icons.Outlined.Delete,
|
||||||
onClick = { onDownloadOptionSelected(SeriesDownloadOption.DELETE_SMART) },
|
onClick = { onDownloadOptionSelected(SeriesDownloadOption.DELETE_SMART) },
|
||||||
modifier = Modifier.testTag(SeriesSmartDownloadButtonTag),
|
modifier = Modifier.testTag(SeriesSmartDownloadButtonTag),
|
||||||
colors = ButtonDefaults.textButtonColors(
|
destructive = true
|
||||||
contentColor = MaterialTheme.colorScheme.error
|
)
|
||||||
)
|
|
||||||
) {
|
|
||||||
Text("Delete Smart Downloads")
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
TextButton(
|
DownloadOptionRow(
|
||||||
|
title = "Smart Downloads",
|
||||||
|
supportingText = "Automatically keep the next unwatched episodes available offline.",
|
||||||
|
icon = Icons.Outlined.AutoAwesome,
|
||||||
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SMART) },
|
onClick = { onDownloadOptionSelected(SeriesDownloadOption.SMART) },
|
||||||
modifier = Modifier.testTag(SeriesSmartDownloadButtonTag)
|
modifier = Modifier.testTag(SeriesSmartDownloadButtonTag)
|
||||||
) {
|
)
|
||||||
Text("Smart Download")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DownloadOptionRow(
|
||||||
|
title: String,
|
||||||
|
supportingText: String,
|
||||||
|
icon: ImageVector,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
destructive: Boolean = false,
|
||||||
|
enabled: Boolean = true,
|
||||||
|
) {
|
||||||
|
val contentColor = if (!enabled) {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
} else if (destructive) {
|
||||||
|
MaterialTheme.colorScheme.error
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
}
|
||||||
|
val supportingColor = if (!enabled) {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
} else if (destructive) {
|
||||||
|
MaterialTheme.colorScheme.error.copy(alpha = 0.8f)
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
}
|
||||||
|
|
||||||
|
ListItem(
|
||||||
|
headlineContent = {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = contentColor
|
||||||
|
)
|
||||||
|
},
|
||||||
|
supportingContent = {
|
||||||
|
Text(
|
||||||
|
text = supportingText,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = supportingColor
|
||||||
|
)
|
||||||
|
},
|
||||||
|
leadingContent = {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = contentColor
|
||||||
|
)
|
||||||
|
},
|
||||||
|
colors = ListItemDefaults.colors(containerColor = Color.Transparent),
|
||||||
|
modifier = modifier.clickable(enabled = enabled, onClick = onClick)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package hu.bbara.purefin.core.feature.downloads
|
||||||
|
|
||||||
|
import hu.bbara.purefin.core.model.MediaUiModel
|
||||||
|
|
||||||
|
data class DownloadedItem(
|
||||||
|
val media: MediaUiModel,
|
||||||
|
)
|
||||||
@@ -48,16 +48,25 @@ class DownloadsViewModel @Inject constructor(
|
|||||||
private val activeDownloadsMap = downloadManager.observeActiveDownloads()
|
private val activeDownloadsMap = downloadManager.observeActiveDownloads()
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyMap())
|
||||||
|
|
||||||
/** Items that are fully downloaded and not currently in progress. */
|
/** Offline items with saved content available, excluding items that are only currently in progress. */
|
||||||
val downloads = combine(
|
val downloads = combine(
|
||||||
offlineCatalogReader.movies,
|
offlineCatalogReader.movies,
|
||||||
offlineCatalogReader.series,
|
offlineCatalogReader.series,
|
||||||
activeDownloadsMap
|
offlineCatalogReader.episodes,
|
||||||
) { movies, series, inProgress ->
|
activeDownloadsMap,
|
||||||
|
) { movies, series, episodes, inProgress ->
|
||||||
movies.values
|
movies.values
|
||||||
.filter { it.id.toString() !in inProgress }
|
.filter { it.id.toString() !in inProgress }
|
||||||
.map { MovieUiModel(it) } +
|
.map { DownloadedItem(media = MovieUiModel(it)) } +
|
||||||
series.values.map { SeriesUiModel(it) }
|
series.values.filter { series ->
|
||||||
|
episodes.values.any { episode ->
|
||||||
|
episode.seriesId == series.id && episode.id.toString() !in inProgress
|
||||||
|
}
|
||||||
|
}.map {
|
||||||
|
DownloadedItem(
|
||||||
|
media = SeriesUiModel(it),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Items currently being downloaded with their progress. */
|
/** Items currently being downloaded with their progress. */
|
||||||
|
|||||||
@@ -29,5 +29,5 @@ android.r8.strictFullModeForKeepRules=false
|
|||||||
android.builtInKotlin=false
|
android.builtInKotlin=false
|
||||||
android.newDsl=false
|
android.newDsl=false
|
||||||
purefinReleaseVersionCode=1000018
|
purefinReleaseVersionCode=1000018
|
||||||
purefinDebugVersionCode=1000000
|
purefinDebugVersionCode=1000001
|
||||||
purefinTvVersionCode=2000009
|
purefinTvVersionCode=2000009
|
||||||
|
|||||||
Reference in New Issue
Block a user