123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- package eu.kanade.presentation.components
- import androidx.compose.animation.core.animateFloatAsState
- import androidx.compose.foundation.combinedClickable
- import androidx.compose.foundation.interaction.MutableInteractionSource
- import androidx.compose.foundation.layout.Box
- import androidx.compose.foundation.layout.padding
- import androidx.compose.foundation.layout.size
- import androidx.compose.material.icons.Icons
- import androidx.compose.material.icons.filled.ArrowDownward
- import androidx.compose.material.icons.filled.CheckCircle
- import androidx.compose.material.ripple.rememberRipple
- import androidx.compose.material3.CircularProgressIndicator
- import androidx.compose.material3.DropdownMenuItem
- import androidx.compose.material3.Icon
- import androidx.compose.material3.MaterialTheme
- import androidx.compose.material3.ProgressIndicatorDefaults
- import androidx.compose.material3.Text
- import androidx.compose.runtime.Composable
- import androidx.compose.runtime.getValue
- import androidx.compose.runtime.mutableStateOf
- import androidx.compose.runtime.remember
- import androidx.compose.runtime.setValue
- import androidx.compose.ui.Alignment
- import androidx.compose.ui.Modifier
- import androidx.compose.ui.graphics.Color
- import androidx.compose.ui.res.stringResource
- import androidx.compose.ui.semantics.Role
- import androidx.compose.ui.unit.dp
- import eu.kanade.presentation.manga.ChapterDownloadAction
- import eu.kanade.presentation.util.secondaryItemAlpha
- import eu.kanade.tachiyomi.R
- import eu.kanade.tachiyomi.data.download.model.Download
- @Composable
- fun ChapterDownloadIndicator(
- modifier: Modifier = Modifier,
- downloadStateProvider: () -> Download.State,
- downloadProgressProvider: () -> Int,
- onClick: (ChapterDownloadAction) -> Unit,
- ) {
- val downloadState = downloadStateProvider()
- val isDownloaded = downloadState == Download.State.DOWNLOADED
- val isDownloading = downloadState != Download.State.NOT_DOWNLOADED
- var isMenuExpanded by remember(downloadState) { mutableStateOf(false) }
- Box(
- modifier = modifier
- .size(IconButtonTokens.StateLayerSize)
- .combinedClickable(
- onLongClick = {
- val chapterDownloadAction = when {
- isDownloaded -> ChapterDownloadAction.DELETE
- isDownloading -> ChapterDownloadAction.CANCEL
- else -> ChapterDownloadAction.START_NOW
- }
- onClick(chapterDownloadAction)
- },
- onClick = {
- if (isDownloaded || isDownloading) {
- isMenuExpanded = true
- } else {
- onClick(ChapterDownloadAction.START)
- }
- },
- role = Role.Button,
- interactionSource = remember { MutableInteractionSource() },
- indication = rememberRipple(
- bounded = false,
- radius = IconButtonTokens.StateLayerSize / 2,
- ),
- ),
- contentAlignment = Alignment.Center,
- ) {
- if (isDownloaded) {
- Icon(
- imageVector = Icons.Default.CheckCircle,
- contentDescription = null,
- modifier = Modifier.size(IndicatorSize),
- tint = MaterialTheme.colorScheme.onSurfaceVariant,
- )
- DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
- DropdownMenuItem(
- text = { Text(text = stringResource(R.string.action_delete)) },
- onClick = {
- onClick(ChapterDownloadAction.DELETE)
- isMenuExpanded = false
- },
- )
- }
- } else {
- val inactiveAlphaModifier = if (!isDownloading) Modifier.secondaryItemAlpha() else Modifier
- val arrowColor: Color
- val strokeColor = MaterialTheme.colorScheme.onSurfaceVariant
- if (isDownloading) {
- val downloadProgress = downloadProgressProvider()
- val indeterminate = downloadState == Download.State.QUEUE ||
- (downloadState == Download.State.DOWNLOADING && downloadProgress == 0)
- if (indeterminate) {
- arrowColor = strokeColor
- CircularProgressIndicator(
- modifier = IndicatorModifier,
- color = strokeColor,
- strokeWidth = IndicatorStrokeWidth,
- )
- } else {
- val animatedProgress by animateFloatAsState(
- targetValue = downloadProgress / 100f,
- animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec,
- )
- arrowColor = if (animatedProgress < 0.5f) {
- strokeColor
- } else {
- MaterialTheme.colorScheme.background
- }
- CircularProgressIndicator(
- progress = animatedProgress,
- modifier = IndicatorModifier,
- color = strokeColor,
- strokeWidth = IndicatorSize / 2,
- )
- }
- DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
- DropdownMenuItem(
- text = { Text(text = stringResource(R.string.action_start_downloading_now)) },
- onClick = {
- onClick(ChapterDownloadAction.START_NOW)
- isMenuExpanded = false
- },
- )
- DropdownMenuItem(
- text = { Text(text = stringResource(R.string.action_cancel)) },
- onClick = {
- onClick(ChapterDownloadAction.CANCEL)
- isMenuExpanded = false
- },
- )
- }
- } else {
- arrowColor = strokeColor
- CircularProgressIndicator(
- progress = 1f,
- modifier = IndicatorModifier.then(inactiveAlphaModifier),
- color = strokeColor,
- strokeWidth = IndicatorStrokeWidth,
- )
- }
- Icon(
- imageVector = Icons.Default.ArrowDownward,
- contentDescription = null,
- modifier = ArrowModifier.then(inactiveAlphaModifier),
- tint = arrowColor,
- )
- }
- }
- }
- private val IndicatorSize = 26.dp
- private val IndicatorPadding = 2.dp
- // To match composable parameter name when used later
- private val IndicatorStrokeWidth = IndicatorPadding
- private val IndicatorModifier = Modifier
- .size(IndicatorSize)
- .padding(IndicatorPadding)
- private val ArrowModifier = Modifier
- .size(IndicatorSize - 7.dp)
|