Download control buttons in list
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.download.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
@@ -9,20 +10,33 @@ import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.databinding.ItemDownloadBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
import org.koitharu.kotatsu.utils.ext.*
|
||||
import org.koitharu.kotatsu.utils.progress.ProgressJob
|
||||
|
||||
fun downloadItemAD(
|
||||
scope: CoroutineScope,
|
||||
coil: ImageLoader,
|
||||
) = adapterDelegateViewBinding<ProgressJob<DownloadState>, ProgressJob<DownloadState>, ItemDownloadBinding>(
|
||||
) = adapterDelegateViewBinding<DownloadItem, DownloadItem, ItemDownloadBinding>(
|
||||
{ inflater, parent -> ItemDownloadBinding.inflate(inflater, parent, false) },
|
||||
) {
|
||||
var job: Job? = null
|
||||
val percentPattern = context.resources.getString(R.string.percent_string_pattern)
|
||||
|
||||
val clickListener = View.OnClickListener { v ->
|
||||
when (v.id) {
|
||||
R.id.button_cancel -> item.cancel()
|
||||
R.id.button_resume -> item.resume()
|
||||
else -> context.startActivity(
|
||||
DetailsActivity.newIntent(context, item.progressValue.manga),
|
||||
)
|
||||
}
|
||||
}
|
||||
binding.buttonCancel.setOnClickListener(clickListener)
|
||||
binding.buttonResume.setOnClickListener(clickListener)
|
||||
itemView.setOnClickListener(clickListener)
|
||||
|
||||
bind {
|
||||
job?.cancel()
|
||||
job = item.progressAsFlow().onFirst { state ->
|
||||
@@ -43,6 +57,8 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = true
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = false
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
is DownloadState.Done -> {
|
||||
binding.textViewStatus.setText(R.string.download_complete)
|
||||
@@ -50,6 +66,8 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = false
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = false
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
is DownloadState.Error -> {
|
||||
binding.textViewStatus.setText(R.string.error_occurred)
|
||||
@@ -58,6 +76,8 @@ fun downloadItemAD(
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.text = state.error.getDisplayMessage(context.resources)
|
||||
binding.textViewDetails.isVisible = true
|
||||
binding.buttonCancel.isVisible = state.canRetry
|
||||
binding.buttonResume.isVisible = state.canRetry
|
||||
}
|
||||
is DownloadState.PostProcessing -> {
|
||||
binding.textViewStatus.setText(R.string.processing_)
|
||||
@@ -65,6 +85,8 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = true
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = false
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
is DownloadState.Preparing -> {
|
||||
binding.textViewStatus.setText(R.string.preparing_)
|
||||
@@ -72,6 +94,8 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = true
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = true
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
is DownloadState.Progress -> {
|
||||
binding.textViewStatus.setText(R.string.manga_downloading_)
|
||||
@@ -82,6 +106,8 @@ fun downloadItemAD(
|
||||
binding.textViewPercent.text = percentPattern.format((state.percent * 100f).format(1))
|
||||
binding.textViewPercent.isVisible = true
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = true
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
is DownloadState.Queued -> {
|
||||
binding.textViewStatus.setText(R.string.queued)
|
||||
@@ -89,6 +115,8 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = false
|
||||
binding.textViewPercent.isVisible = false
|
||||
binding.textViewDetails.isVisible = false
|
||||
binding.buttonCancel.isVisible = true
|
||||
binding.buttonResume.isVisible = false
|
||||
}
|
||||
}
|
||||
}.launchIn(scope)
|
||||
|
||||
@@ -5,12 +5,14 @@ import coil.ImageLoader
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
import org.koitharu.kotatsu.utils.progress.ProgressJob
|
||||
import org.koitharu.kotatsu.utils.progress.PausingProgressJob
|
||||
|
||||
typealias DownloadItem = PausingProgressJob<DownloadState>
|
||||
|
||||
class DownloadsAdapter(
|
||||
scope: CoroutineScope,
|
||||
coil: ImageLoader,
|
||||
) : AsyncListDifferDelegationAdapter<ProgressJob<DownloadState>>(DiffCallback()) {
|
||||
) : AsyncListDifferDelegationAdapter<DownloadItem>(DiffCallback()) {
|
||||
|
||||
init {
|
||||
delegatesManager.addDelegate(downloadItemAD(scope, coil))
|
||||
@@ -21,18 +23,18 @@ class DownloadsAdapter(
|
||||
return items[position].progressValue.startId.toLong()
|
||||
}
|
||||
|
||||
private class DiffCallback : DiffUtil.ItemCallback<ProgressJob<DownloadState>>() {
|
||||
private class DiffCallback : DiffUtil.ItemCallback<DownloadItem>() {
|
||||
|
||||
override fun areItemsTheSame(
|
||||
oldItem: ProgressJob<DownloadState>,
|
||||
newItem: ProgressJob<DownloadState>,
|
||||
oldItem: DownloadItem,
|
||||
newItem: DownloadItem,
|
||||
): Boolean {
|
||||
return oldItem.progressValue.startId == newItem.progressValue.startId
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ProgressJob<DownloadState>,
|
||||
newItem: ProgressJob<DownloadState>,
|
||||
oldItem: DownloadItem,
|
||||
newItem: DownloadItem,
|
||||
): Boolean {
|
||||
return oldItem.progressValue == newItem.progressValue
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.core.text.HtmlCompat
|
||||
import androidx.core.text.htmlEncode
|
||||
import androidx.core.text.parseAsHtml
|
||||
import androidx.core.util.forEach
|
||||
import androidx.core.util.isNotEmpty
|
||||
import androidx.core.util.size
|
||||
import com.google.android.material.R as materialR
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -98,7 +99,7 @@ class DownloadNotification(private val context: Context) {
|
||||
style.addLine(
|
||||
context.getString(
|
||||
R.string.download_summary_pattern,
|
||||
state.manga.title.ellipsize(10).htmlEncode(),
|
||||
state.manga.title.ellipsize(16).htmlEncode(),
|
||||
summary.htmlEncode(),
|
||||
).parseAsHtml(HtmlCompat.FROM_HTML_MODE_LEGACY),
|
||||
)
|
||||
@@ -113,15 +114,17 @@ class DownloadNotification(private val context: Context) {
|
||||
when (progress) {
|
||||
1f -> groupBuilder.setProgress(0, 0, false)
|
||||
0f -> groupBuilder.setProgress(1, 0, true)
|
||||
else -> groupBuilder.setProgress(100, (progress * 100f).toInt(), progress == 0f)
|
||||
else -> groupBuilder.setProgress(100, (progress * 100f).toInt(), false)
|
||||
}
|
||||
return groupBuilder.build()
|
||||
}
|
||||
|
||||
fun detach() {
|
||||
manager.cancel(ID_GROUP)
|
||||
val notification = buildGroupNotification()
|
||||
manager.notify(ID_GROUP_DETACHED, notification)
|
||||
if (states.isNotEmpty() && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
val notification = buildGroupNotification()
|
||||
manager.notify(ID_GROUP_DETACHED, notification)
|
||||
}
|
||||
}
|
||||
|
||||
fun newItem(startId: Int) = Item(startId)
|
||||
@@ -171,6 +174,8 @@ class DownloadNotification(private val context: Context) {
|
||||
builder.setStyle(null)
|
||||
builder.setLargeIcon(state.cover?.toBitmap())
|
||||
builder.clearActions()
|
||||
builder.setSubText(null)
|
||||
builder.setShowWhen(false)
|
||||
builder.setVisibility(
|
||||
if (state.manga.isNsfw) {
|
||||
NotificationCompat.VISIBILITY_PRIVATE
|
||||
@@ -196,6 +201,8 @@ class DownloadNotification(private val context: Context) {
|
||||
builder.setCategory(null)
|
||||
builder.setStyle(null)
|
||||
builder.setOngoing(false)
|
||||
builder.setShowWhen(true)
|
||||
builder.setWhen(System.currentTimeMillis())
|
||||
builder.priority = NotificationCompat.PRIORITY_DEFAULT
|
||||
}
|
||||
is DownloadState.Error -> {
|
||||
@@ -207,6 +214,8 @@ class DownloadNotification(private val context: Context) {
|
||||
builder.setAutoCancel(!state.canRetry)
|
||||
builder.setOngoing(state.canRetry)
|
||||
builder.setCategory(NotificationCompat.CATEGORY_ERROR)
|
||||
builder.setShowWhen(true)
|
||||
builder.setWhen(System.currentTimeMillis())
|
||||
builder.setStyle(NotificationCompat.BigTextStyle().bigText(message))
|
||||
if (state.canRetry) {
|
||||
builder.addAction(cancelAction)
|
||||
@@ -239,12 +248,13 @@ class DownloadNotification(private val context: Context) {
|
||||
}
|
||||
is DownloadState.Progress -> {
|
||||
builder.setProgress(state.max, state.progress, false)
|
||||
val percent = context.getString(R.string.percent_string_pattern, (state.percent * 100).format())
|
||||
if (timeLeft > 0L) {
|
||||
val eta = DateUtils.getRelativeTimeSpanString(timeLeft, 0L, DateUtils.SECOND_IN_MILLIS)
|
||||
builder.setContentText(eta)
|
||||
builder.setSubText(percent)
|
||||
} else {
|
||||
val percent = (state.percent * 100).format()
|
||||
builder.setContentText(context.getString(R.string.percent_string_pattern, percent))
|
||||
builder.setContentText(percent)
|
||||
}
|
||||
builder.setCategory(NotificationCompat.CATEGORY_PROGRESS)
|
||||
builder.setStyle(null)
|
||||
|
||||
@@ -129,6 +129,8 @@ class DownloadService : BaseService() {
|
||||
}
|
||||
if (job.isCancelled) {
|
||||
notificationItem.dismiss()
|
||||
jobs.remove(startId)
|
||||
jobCount.value = jobs.size
|
||||
} else {
|
||||
notificationItem.notify(job.progressValue, -1L)
|
||||
}
|
||||
|
||||
@@ -77,10 +77,10 @@
|
||||
android:id="@+id/textView_percent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
app:layout_constraintBaseline_toBaselineOf="@id/textView_status"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginEnd="8dp"
|
||||
tools:text="25%" />
|
||||
|
||||
<TextView
|
||||
@@ -98,4 +98,30 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_status"
|
||||
tools:text="@tools:sample/lorem[3]" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_cancel"
|
||||
style="?buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@android:string/cancel"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_details"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_resume"
|
||||
style="?buttonBarButtonStyle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/try_again"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_details"
|
||||
tools:visibility="visible" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -317,5 +317,4 @@
|
||||
<string name="data_deletion">Удаление данных</string>
|
||||
<string name="clear_cookies_summary">Может помочь в случае каких-либо проблем. Все авторизации будут аннулированы</string>
|
||||
<string name="show_all">Показать все</string>
|
||||
<string name="downloading_manga">Downloading manga</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user