ChapterDownloadIndicator: Fixes and improvements (#7485)
* Increased touch target * Fix downloaded icon smaller than other states * Deferred state reads to minimize recompose works * Move things around to eliminate unnecessary elements
This commit is contained in:
parent
34906a7425
commit
e56f6c1017
@ -1,21 +1,22 @@
|
|||||||
package eu.kanade.presentation.components
|
package eu.kanade.presentation.components
|
||||||
|
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
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.Box
|
||||||
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.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowDownward
|
import androidx.compose.material.icons.filled.ArrowDownward
|
||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.CompositionLocalProvider
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@ -24,6 +25,7 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.semantics.Role
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import eu.kanade.presentation.manga.ChapterDownloadAction
|
import eu.kanade.presentation.manga.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||||
@ -33,23 +35,18 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
|||||||
@Composable
|
@Composable
|
||||||
fun ChapterDownloadIndicator(
|
fun ChapterDownloadIndicator(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
downloadState: Download.State,
|
downloadStateProvider: () -> Download.State,
|
||||||
downloadProgress: Int,
|
downloadProgressProvider: () -> Int,
|
||||||
onClick: (ChapterDownloadAction) -> Unit,
|
onClick: (ChapterDownloadAction) -> Unit,
|
||||||
) {
|
) {
|
||||||
Box(modifier = modifier, contentAlignment = Alignment.Center) {
|
val downloadState = downloadStateProvider()
|
||||||
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
|
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
||||||
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
val isDownloading = downloadState != Download.State.NOT_DOWNLOADED
|
||||||
val isDownloading = downloadState != Download.State.NOT_DOWNLOADED
|
var isMenuExpanded by remember(downloadState) { mutableStateOf(false) }
|
||||||
var isMenuExpanded by remember(downloadState) { mutableStateOf(false) }
|
Box(
|
||||||
IconButton(
|
modifier = modifier
|
||||||
onClick = {
|
.size(IconButtonTokens.StateLayerSize)
|
||||||
if (isDownloaded || isDownloading) {
|
.combinedClickable(
|
||||||
isMenuExpanded = true
|
|
||||||
} else {
|
|
||||||
onClick(ChapterDownloadAction.START)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
val chapterDownloadAction = when {
|
val chapterDownloadAction = when {
|
||||||
isDownloaded -> ChapterDownloadAction.DELETE
|
isDownloaded -> ChapterDownloadAction.DELETE
|
||||||
@ -58,93 +55,101 @@ fun ChapterDownloadIndicator(
|
|||||||
}
|
}
|
||||||
onClick(chapterDownloadAction)
|
onClick(chapterDownloadAction)
|
||||||
},
|
},
|
||||||
) {
|
onClick = {
|
||||||
val indicatorModifier = Modifier
|
if (isDownloaded || isDownloading) {
|
||||||
.size(IndicatorSize)
|
isMenuExpanded = true
|
||||||
.padding(IndicatorPadding)
|
|
||||||
if (isDownloaded) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.CheckCircle,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = indicatorModifier,
|
|
||||||
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 arrowModifier = Modifier
|
|
||||||
.size(IndicatorSize - 7.dp)
|
|
||||||
.then(inactiveAlphaModifier)
|
|
||||||
val arrowColor: Color
|
|
||||||
val strokeColor = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
if (isDownloading) {
|
|
||||||
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,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
arrowColor = strokeColor
|
onClick(ChapterDownloadAction.START)
|
||||||
CircularProgressIndicator(
|
|
||||||
progress = 1f,
|
|
||||||
modifier = indicatorModifier.then(inactiveAlphaModifier),
|
|
||||||
color = strokeColor,
|
|
||||||
strokeWidth = IndicatorStrokeWidth,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Icon(
|
},
|
||||||
imageVector = Icons.Default.ArrowDownward,
|
role = Role.Button,
|
||||||
contentDescription = null,
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
modifier = arrowModifier,
|
indication = rememberRipple(
|
||||||
tint = arrowColor,
|
bounded = false,
|
||||||
)
|
radius = IconButtonTokens.StateLayerSize / 2,
|
||||||
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
|
),
|
||||||
DropdownMenuItem(
|
),
|
||||||
text = { Text(text = stringResource(R.string.action_start_downloading_now)) },
|
contentAlignment = Alignment.Center,
|
||||||
onClick = {
|
) {
|
||||||
onClick(ChapterDownloadAction.START_NOW)
|
if (isDownloaded) {
|
||||||
isMenuExpanded = false
|
Icon(
|
||||||
},
|
imageVector = Icons.Default.CheckCircle,
|
||||||
)
|
contentDescription = null,
|
||||||
DropdownMenuItem(
|
modifier = Modifier.size(IndicatorSize),
|
||||||
text = { Text(text = stringResource(R.string.action_cancel)) },
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
onClick = {
|
)
|
||||||
onClick(ChapterDownloadAction.CANCEL)
|
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
|
||||||
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,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,3 +159,9 @@ private val IndicatorPadding = 2.dp
|
|||||||
|
|
||||||
// To match composable parameter name when used later
|
// To match composable parameter name when used later
|
||||||
private val IndicatorStrokeWidth = IndicatorPadding
|
private val IndicatorStrokeWidth = IndicatorPadding
|
||||||
|
|
||||||
|
private val IndicatorModifier = Modifier
|
||||||
|
.size(IndicatorSize)
|
||||||
|
.padding(IndicatorPadding)
|
||||||
|
private val ArrowModifier = Modifier
|
||||||
|
.size(IndicatorSize - 7.dp)
|
||||||
|
@ -685,8 +685,8 @@ private fun LazyListScope.sharedChapterItems(
|
|||||||
read = chapter.read,
|
read = chapter.read,
|
||||||
bookmark = chapter.bookmark,
|
bookmark = chapter.bookmark,
|
||||||
selected = selected.contains(chapterItem),
|
selected = selected.contains(chapterItem),
|
||||||
downloadState = downloadState,
|
downloadStateProvider = { downloadState },
|
||||||
downloadProgress = downloadProgress,
|
downloadProgressProvider = { downloadProgress },
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
val dispatched = onChapterItemLongClick(
|
val dispatched = onChapterItemLongClick(
|
||||||
chapterItem = chapterItem,
|
chapterItem = chapterItem,
|
||||||
|
@ -44,8 +44,8 @@ fun MangaChapterListItem(
|
|||||||
read: Boolean,
|
read: Boolean,
|
||||||
bookmark: Boolean,
|
bookmark: Boolean,
|
||||||
selected: Boolean,
|
selected: Boolean,
|
||||||
downloadState: Download.State,
|
downloadStateProvider: () -> Download.State,
|
||||||
downloadProgress: Int,
|
downloadProgressProvider: () -> Int,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onDownloadClick: ((ChapterDownloadAction) -> Unit)?,
|
onDownloadClick: ((ChapterDownloadAction) -> Unit)?,
|
||||||
@ -127,8 +127,8 @@ fun MangaChapterListItem(
|
|||||||
if (onDownloadClick != null) {
|
if (onDownloadClick != null) {
|
||||||
ChapterDownloadIndicator(
|
ChapterDownloadIndicator(
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
modifier = Modifier.padding(start = 4.dp),
|
||||||
downloadState = downloadState,
|
downloadStateProvider = downloadStateProvider,
|
||||||
downloadProgress = downloadProgress,
|
downloadProgressProvider = downloadProgressProvider,
|
||||||
onClick = onDownloadClick,
|
onClick = onDownloadClick,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ class ChapterDownloadView @JvmOverloads constructor(
|
|||||||
override fun Content() {
|
override fun Content() {
|
||||||
TachiyomiTheme {
|
TachiyomiTheme {
|
||||||
ChapterDownloadIndicator(
|
ChapterDownloadIndicator(
|
||||||
downloadState = state,
|
downloadStateProvider = { state },
|
||||||
downloadProgress = progress,
|
downloadProgressProvider = { progress },
|
||||||
onClick = listener,
|
onClick = listener,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user