mirror of
https://github.com/aniyomiorg/aniyomi.git
synced 2024-11-24 05:38:01 +03:00
ChapterDownloadView: Convert to compose (#7354)
This commit is contained in:
parent
8e985eb0db
commit
a77bce7b37
6 changed files with 187 additions and 140 deletions
|
@ -0,0 +1,146 @@
|
|||
package eu.kanade.presentation.components
|
||||
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
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.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.LocalMinimumTouchTargetEnforcement
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProgressIndicatorDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
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.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,
|
||||
downloadState: Download.State,
|
||||
downloadProgress: Int,
|
||||
onClick: (ChapterDownloadAction) -> Unit,
|
||||
) {
|
||||
Box(modifier = modifier, contentAlignment = Alignment.Center) {
|
||||
CompositionLocalProvider(LocalMinimumTouchTargetEnforcement provides false) {
|
||||
val isDownloaded = downloadState == Download.State.DOWNLOADED
|
||||
val isDownloading = downloadState != Download.State.NOT_DOWNLOADED
|
||||
var isMenuExpanded by remember(downloadState) { mutableStateOf(false) }
|
||||
IconButton(
|
||||
onClick = {
|
||||
if (isDownloaded || isDownloading) {
|
||||
isMenuExpanded = true
|
||||
} else {
|
||||
onClick(ChapterDownloadAction.START)
|
||||
}
|
||||
},
|
||||
) {
|
||||
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(id = R.string.action_delete)) },
|
||||
onClick = {
|
||||
onClick(ChapterDownloadAction.DELETE)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val progressIndicatorModifier = Modifier
|
||||
.size(IndicatorSize)
|
||||
.padding(IndicatorStrokeWidth)
|
||||
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 = progressIndicatorModifier,
|
||||
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 = progressIndicatorModifier,
|
||||
color = strokeColor,
|
||||
strokeWidth = IndicatorSize / 2,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
arrowColor = strokeColor
|
||||
CircularProgressIndicator(
|
||||
progress = 1f,
|
||||
modifier = progressIndicatorModifier.then(inactiveAlphaModifier),
|
||||
color = strokeColor,
|
||||
strokeWidth = IndicatorStrokeWidth,
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowDownward,
|
||||
contentDescription = null,
|
||||
modifier = arrowModifier,
|
||||
tint = arrowColor,
|
||||
)
|
||||
DropdownMenu(expanded = isMenuExpanded, onDismissRequest = { isMenuExpanded = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(id = R.string.action_start_downloading_now)) },
|
||||
onClick = {
|
||||
onClick(ChapterDownloadAction.START_NOW)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(id = R.string.action_cancel)) },
|
||||
onClick = {
|
||||
onClick(ChapterDownloadAction.CANCEL)
|
||||
isMenuExpanded = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IndicatorSize = 26.dp
|
||||
private val IndicatorStrokeWidth = 2.dp
|
|
@ -4,3 +4,10 @@ enum class EditCoverAction {
|
|||
EDIT,
|
||||
DELETE,
|
||||
}
|
||||
|
||||
enum class ChapterDownloadAction {
|
||||
START,
|
||||
START_NOW,
|
||||
CANCEL,
|
||||
DELETE,
|
||||
}
|
||||
|
|
|
@ -1,95 +1,40 @@
|
|||
package eu.kanade.tachiyomi.ui.manga.chapter
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.progressindicator.BaseProgressIndicator
|
||||
import eu.kanade.tachiyomi.R
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.platform.AbstractComposeView
|
||||
import eu.kanade.presentation.components.ChapterDownloadIndicator
|
||||
import eu.kanade.presentation.manga.ChapterDownloadAction
|
||||
import eu.kanade.presentation.theme.TachiyomiTheme
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.databinding.ChapterDownloadViewBinding
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.getThemeColor
|
||||
import eu.kanade.tachiyomi.util.view.setVectorCompat
|
||||
|
||||
class ChapterDownloadView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
FrameLayout(context, attrs) {
|
||||
class ChapterDownloadView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyle: Int = 0,
|
||||
) : AbstractComposeView(context, attrs, defStyle) {
|
||||
|
||||
private val binding: ChapterDownloadViewBinding =
|
||||
ChapterDownloadViewBinding.inflate(LayoutInflater.from(context), this, false)
|
||||
private var state by mutableStateOf(Download.State.NOT_DOWNLOADED)
|
||||
private var progress by mutableStateOf(0)
|
||||
|
||||
private var state: Download.State? = null
|
||||
private var progress = -1
|
||||
var listener: (ChapterDownloadAction) -> Unit = {}
|
||||
|
||||
init {
|
||||
addView(binding.root)
|
||||
}
|
||||
|
||||
fun setState(state: Download.State, progress: Int = -1) {
|
||||
val isDirty = this.state?.value != state.value || this.progress != progress
|
||||
if (isDirty) {
|
||||
updateLayout(state, progress)
|
||||
@Composable
|
||||
override fun Content() {
|
||||
TachiyomiTheme {
|
||||
ChapterDownloadIndicator(
|
||||
downloadState = state,
|
||||
downloadProgress = progress,
|
||||
onClick = listener,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLayout(state: Download.State, progress: Int) {
|
||||
binding.downloadIcon.isVisible = state == Download.State.NOT_DOWNLOADED ||
|
||||
state == Download.State.DOWNLOADING || state == Download.State.QUEUE
|
||||
binding.downloadIcon.imageTintList = if (state == Download.State.DOWNLOADING && progress > 0) {
|
||||
ColorStateList.valueOf(context.getThemeColor(android.R.attr.colorBackground))
|
||||
} else {
|
||||
ColorStateList.valueOf(context.getThemeColor(android.R.attr.textColorHint))
|
||||
}
|
||||
|
||||
binding.downloadProgress.apply {
|
||||
val shouldBeVisible = state == Download.State.DOWNLOADING ||
|
||||
state == Download.State.NOT_DOWNLOADED || state == Download.State.QUEUE
|
||||
if (shouldBeVisible) {
|
||||
hideAnimationBehavior = BaseProgressIndicator.HIDE_NONE
|
||||
if (state == Download.State.NOT_DOWNLOADED || state == Download.State.QUEUE) {
|
||||
trackThickness = 2.dpToPx
|
||||
setIndicatorColor(context.getThemeColor(android.R.attr.textColorHint))
|
||||
if (state == Download.State.NOT_DOWNLOADED) {
|
||||
if (isIndeterminate) {
|
||||
hide()
|
||||
isIndeterminate = false
|
||||
}
|
||||
setProgressCompat(100, false)
|
||||
} else if (!isIndeterminate) {
|
||||
hide()
|
||||
isIndeterminate = true
|
||||
show()
|
||||
}
|
||||
} else if (state == Download.State.DOWNLOADING) {
|
||||
if (isIndeterminate) {
|
||||
hide()
|
||||
}
|
||||
trackThickness = 12.dpToPx
|
||||
setIndicatorColor(context.getThemeColor(android.R.attr.textColorPrimary))
|
||||
setProgressCompat(progress, true)
|
||||
}
|
||||
show()
|
||||
} else {
|
||||
hideAnimationBehavior = BaseProgressIndicator.HIDE_OUTWARD
|
||||
hide()
|
||||
}
|
||||
}
|
||||
|
||||
binding.downloadStatusIcon.apply {
|
||||
if (state == Download.State.DOWNLOADED || state == Download.State.ERROR) {
|
||||
isVisible = true
|
||||
if (state == Download.State.DOWNLOADED) {
|
||||
setVectorCompat(R.drawable.ic_check_circle_24dp, android.R.attr.textColorPrimary)
|
||||
} else {
|
||||
setVectorCompat(R.drawable.ic_error_outline_24dp, R.attr.colorError)
|
||||
}
|
||||
} else {
|
||||
isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
fun setState(state: Download.State, progress: Int = 0) {
|
||||
this.state = state
|
||||
this.progress = progress
|
||||
}
|
||||
|
|
|
@ -22,13 +22,7 @@ class ChapterHolder(
|
|||
private val binding = ChaptersItemBinding.bind(view)
|
||||
|
||||
init {
|
||||
binding.download.setOnClickListener {
|
||||
onDownloadClick(it, bindingAdapterPosition)
|
||||
}
|
||||
binding.download.setOnLongClickListener {
|
||||
onDownloadLongClick(bindingAdapterPosition)
|
||||
true
|
||||
}
|
||||
binding.download.listener = downloadActionListener
|
||||
}
|
||||
|
||||
fun bind(item: ChapterItem, manga: Manga) {
|
||||
|
|
|
@ -2,58 +2,19 @@ package eu.kanade.tachiyomi.ui.manga.chapter.base
|
|||
|
||||
import android.view.View
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.util.view.popupMenu
|
||||
import eu.kanade.presentation.manga.ChapterDownloadAction
|
||||
|
||||
open class BaseChapterHolder(
|
||||
view: View,
|
||||
private val adapter: BaseChaptersAdapter<*>,
|
||||
) : FlexibleViewHolder(view, adapter) {
|
||||
|
||||
fun onDownloadClick(view: View, position: Int) {
|
||||
val item = adapter.getItem(position) as? BaseChapterItem<*, *> ?: return
|
||||
when (item.status) {
|
||||
Download.State.NOT_DOWNLOADED, Download.State.ERROR -> {
|
||||
adapter.clickListener.downloadChapter(position)
|
||||
}
|
||||
else -> {
|
||||
view.popupMenu(
|
||||
R.menu.chapter_download,
|
||||
initMenu = {
|
||||
// Download.State.DOWNLOADED
|
||||
findItem(R.id.delete_download).isVisible = item.status == Download.State.DOWNLOADED
|
||||
|
||||
// Download.State.DOWNLOADING, Download.State.QUEUE
|
||||
findItem(R.id.cancel_download).isVisible = item.status != Download.State.DOWNLOADED
|
||||
|
||||
// Download.State.QUEUE
|
||||
findItem(R.id.start_download).isVisible = item.status == Download.State.QUEUE
|
||||
},
|
||||
onMenuItemClick = {
|
||||
if (itemId == R.id.start_download) {
|
||||
adapter.clickListener.startDownloadNow(position)
|
||||
} else {
|
||||
adapter.clickListener.deleteChapter(position)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onDownloadLongClick(position: Int) {
|
||||
val item = adapter.getItem(position) as? BaseChapterItem<*, *> ?: return
|
||||
when (item.status) {
|
||||
Download.State.NOT_DOWNLOADED, Download.State.ERROR -> {
|
||||
adapter.clickListener.downloadChapter(position)
|
||||
}
|
||||
Download.State.DOWNLOADED, Download.State.DOWNLOADING -> {
|
||||
adapter.clickListener.deleteChapter(position)
|
||||
}
|
||||
// Download.State.QUEUE
|
||||
else -> {
|
||||
adapter.clickListener.startDownloadNow(position)
|
||||
val downloadActionListener: (ChapterDownloadAction) -> Unit = { action ->
|
||||
when (action) {
|
||||
ChapterDownloadAction.START -> adapter.clickListener.downloadChapter(bindingAdapterPosition)
|
||||
ChapterDownloadAction.START_NOW -> adapter.clickListener.startDownloadNow(bindingAdapterPosition)
|
||||
ChapterDownloadAction.CANCEL, ChapterDownloadAction.DELETE -> {
|
||||
adapter.clickListener.deleteChapter(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,13 +27,7 @@ class UpdatesHolder(private val view: View, private val adapter: UpdatesAdapter)
|
|||
adapter.coverClickListener.onCoverClick(bindingAdapterPosition)
|
||||
}
|
||||
|
||||
binding.download.setOnClickListener {
|
||||
onDownloadClick(it, bindingAdapterPosition)
|
||||
}
|
||||
binding.download.setOnLongClickListener {
|
||||
onDownloadLongClick(bindingAdapterPosition)
|
||||
true
|
||||
}
|
||||
binding.download.listener = downloadActionListener
|
||||
}
|
||||
|
||||
fun bind(item: UpdatesItem) {
|
||||
|
|
Loading…
Reference in a new issue