diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/PausingProgressJob.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/PausingProgressJob.kt deleted file mode 100644 index 7641646f8..000000000 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/progress/PausingProgressJob.kt +++ /dev/null @@ -1,26 +0,0 @@ -package org.koitharu.kotatsu.core.util.progress - -import androidx.annotation.AnyThread -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.StateFlow -import org.koitharu.kotatsu.download.ui.worker.PausingHandle - -class PausingProgressJob

( - job: Job, - progress: StateFlow

, - private val pausingHandle: PausingHandle, -) : ProgressJob

(job, progress) { - - @get:AnyThread - val isPaused: Boolean - get() = pausingHandle.isPaused - - @AnyThread - suspend fun awaitResumed() = pausingHandle.awaitResumed() - - @AnyThread - fun pause() = pausingHandle.pause() - - @AnyThread - fun resume() = pausingHandle.resume() -} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt index 0dc006845..cc87f160b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemAD.kt @@ -44,7 +44,8 @@ fun downloadItemAD( override fun onClick(v: View) { when (v.id) { R.id.button_cancel -> listener.onCancelClick(item) - R.id.button_resume -> listener.onResumeClick(item) + R.id.button_resume -> listener.onResumeClick(item, skip = false) + R.id.button_skip -> listener.onResumeClick(item, skip = true) R.id.button_pause -> listener.onPauseClick(item) R.id.imageView_expand -> listener.onExpandClick(item) else -> listener.onItemClick(item, v) @@ -62,6 +63,7 @@ fun downloadItemAD( binding.buttonCancel.setOnClickListener(clickListener) binding.buttonPause.setOnClickListener(clickListener) binding.buttonResume.setOnClickListener(clickListener) + binding.buttonSkip.setOnClickListener(clickListener) binding.imageViewExpand.setOnClickListener(clickListener) itemView.setOnClickListener(clickListener) itemView.setOnLongClickListener(clickListener) @@ -120,6 +122,7 @@ fun downloadItemAD( binding.textViewDetails.isVisible = false binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = false + binding.buttonSkip.isVisible = false binding.buttonPause.isVisible = false } @@ -134,9 +137,10 @@ fun downloadItemAD( binding.progressBar.setProgressCompat(item.progress, payloads.isNotEmpty()) binding.textViewPercent.text = percentPattern.format((item.percent * 100f).format(1)) binding.textViewPercent.isVisible = true - binding.textViewDetails.textAndVisible = item.getEtaString() + binding.textViewDetails.textAndVisible = if (item.isPaused) item.error else item.getEtaString() binding.buttonCancel.isVisible = true binding.buttonResume.isVisible = item.isPaused + binding.buttonSkip.isVisible = item.isPaused && item.error != null binding.buttonPause.isVisible = item.canPause } @@ -158,6 +162,7 @@ fun downloadItemAD( } binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false + binding.buttonSkip.isVisible = false binding.buttonPause.isVisible = false } @@ -170,6 +175,7 @@ fun downloadItemAD( binding.textViewDetails.textAndVisible = item.error binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false + binding.buttonSkip.isVisible = false binding.buttonPause.isVisible = false } @@ -182,6 +188,7 @@ fun downloadItemAD( binding.textViewDetails.isVisible = false binding.buttonCancel.isVisible = false binding.buttonResume.isVisible = false + binding.buttonSkip.isVisible = false binding.buttonPause.isVisible = false } } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemListener.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemListener.kt index 547c64381..449911419 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemListener.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadItemListener.kt @@ -8,7 +8,7 @@ interface DownloadItemListener : OnListItemClickListener { fun onPauseClick(item: DownloadItemModel) - fun onResumeClick(item: DownloadItemModel) + fun onResumeClick(item: DownloadItemModel, skip: Boolean) fun onExpandClick(item: DownloadItemModel) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt index 6b0051b2e..f0b51a621 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsActivity.kt @@ -105,8 +105,8 @@ class DownloadsActivity : BaseActivity(), sendBroadcast(PausingReceiver.getPauseIntent(this, item.id)) } - override fun onResumeClick(item: DownloadItemModel) { - sendBroadcast(PausingReceiver.getResumeIntent(this, item.id)) + override fun onResumeClick(item: DownloadItemModel, skip: Boolean) { + sendBroadcast(PausingReceiver.getResumeIntent(this, item.id, skip)) } override fun onSelectionChanged(controller: ListSelectionController, count: Int) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsViewModel.kt index 987257c8d..559723c4e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/list/DownloadsViewModel.kt @@ -143,7 +143,7 @@ class DownloadsViewModel @Inject constructor( var isResumed = false for (work in snapshot) { if (work.workState == WorkInfo.State.RUNNING && work.isPaused) { - workScheduler.resume(work.id) + workScheduler.resume(work.id, skipError = false) isResumed = true } } @@ -156,7 +156,7 @@ class DownloadsViewModel @Inject constructor( val snapshot = works.value ?: return for (work in snapshot) { if (work.id.mostSignificantBits in ids) { - workScheduler.resume(work.id) + workScheduler.resume(work.id, skipError = false) } } onActionDone.call(ReversibleAction(R.string.downloads_resumed, null)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt index 7f2e3d564..aa23dc794 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadNotificationFactory.kt @@ -82,7 +82,15 @@ class DownloadNotificationFactory @AssistedInject constructor( NotificationCompat.Action( R.drawable.ic_action_resume, context.getString(R.string.resume), - PausingReceiver.createResumePendingIntent(context, uuid), + PausingReceiver.createResumePendingIntent(context, uuid, skipError = false), + ) + } + + private val actionSkip by lazy { + NotificationCompat.Action( + R.drawable.ic_action_skip, + context.getString(R.string.skip), + PausingReceiver.createResumePendingIntent(context, uuid, skipError = true), ) } @@ -163,6 +171,9 @@ class DownloadNotificationFactory @AssistedInject constructor( builder.setSmallIcon(R.drawable.ic_stat_paused) builder.addAction(actionCancel) builder.addAction(actionResume) + if (state.error != null) { + builder.addAction(actionSkip) + } } state.error != null -> { // error, final state diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt index fe7e50dea..72825f174 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/DownloadWorker.kt @@ -198,7 +198,7 @@ class DownloadWorker @AssistedInject constructor( } val pages = runFailsafe { repo.getPages(chapter) - } + } ?: continue val pageCounter = AtomicInteger(0) channelFlow { val semaphore = Semaphore(MAX_PAGES_PARALLELISM) @@ -264,7 +264,7 @@ class DownloadWorker @AssistedInject constructor( private suspend fun runFailsafe( block: suspend () -> R, - ): R { + ): R? { checkIsPaused() var countDown = MAX_FAILSAFE_ATTEMPTS failsafe@ while (true) { @@ -284,6 +284,9 @@ class DownloadWorker @AssistedInject constructor( pausingHandle.pause() try { pausingHandle.awaitResumed() + if (pausingHandle.skipCurrentError()) { + return null + } } finally { publishState(currentState.copy(isPaused = false, error = null)) } @@ -448,8 +451,8 @@ class DownloadWorker @AssistedInject constructor( context.sendBroadcast(intent) } - fun resume(id: UUID) { - val intent = PausingReceiver.getResumeIntent(context, id) + fun resume(id: UUID, skipError: Boolean) { + val intent = PausingReceiver.getResumeIntent(context, id, skipError) context.sendBroadcast(intent) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingHandle.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingHandle.kt index c73cd6bbe..0c9eb6653 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingHandle.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingHandle.kt @@ -10,6 +10,7 @@ import kotlin.coroutines.CoroutineContext class PausingHandle : AbstractCoroutineContextElement(PausingHandle) { private val paused = MutableStateFlow(false) + private val isSkipError = MutableStateFlow(false) @get:AnyThread val isPaused: Boolean @@ -26,7 +27,8 @@ class PausingHandle : AbstractCoroutineContextElement(PausingHandle) { } @AnyThread - fun resume() { + fun resume(skipError: Boolean) { + isSkipError.value = skipError paused.value = false } @@ -36,6 +38,8 @@ class PausingHandle : AbstractCoroutineContextElement(PausingHandle) { } } + fun skipCurrentError(): Boolean = isSkipError.compareAndSet(expect = true, update = false) + companion object : CoroutineContext.Key { suspend fun current() = checkNotNull(currentCoroutineContext()[this]) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingReceiver.kt b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingReceiver.kt index 8bf4298d0..b86328610 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingReceiver.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/download/ui/worker/PausingReceiver.kt @@ -21,7 +21,7 @@ class PausingReceiver( return } when (intent.action) { - ACTION_RESUME -> pausingHandle.resume() + ACTION_RESUME -> pausingHandle.resume(intent.getBooleanExtra(EXTRA_SKIP_ERROR, false)) ACTION_PAUSE -> pausingHandle.pause() } } @@ -31,6 +31,7 @@ class PausingReceiver( private const val ACTION_PAUSE = "org.koitharu.kotatsu.download.PAUSE" private const val ACTION_RESUME = "org.koitharu.kotatsu.download.RESUME" private const val EXTRA_UUID = "uuid" + private const val EXTRA_SKIP_ERROR = "skip" private const val SCHEME = "workuid" fun createIntentFilter(id: UUID) = IntentFilter().apply { @@ -45,10 +46,11 @@ class PausingReceiver( .setPackage(context.packageName) .putExtra(EXTRA_UUID, id.toString()) - fun getResumeIntent(context: Context, id: UUID) = Intent(ACTION_RESUME) + fun getResumeIntent(context: Context, id: UUID, skipError: Boolean) = Intent(ACTION_RESUME) .setData(Uri.parse("$SCHEME://$id")) .setPackage(context.packageName) .putExtra(EXTRA_UUID, id.toString()) + .putExtra(EXTRA_SKIP_ERROR, skipError) fun createPausePendingIntent(context: Context, id: UUID) = PendingIntentCompat.getBroadcast( context, @@ -58,12 +60,13 @@ class PausingReceiver( false, ) - fun createResumePendingIntent(context: Context, id: UUID) = PendingIntentCompat.getBroadcast( - context, - 0, - getResumeIntent(context, id), - 0, - false, - ) + fun createResumePendingIntent(context: Context, id: UUID, skipError: Boolean) = + PendingIntentCompat.getBroadcast( + context, + 0, + getResumeIntent(context, id, skipError), + 0, + false, + ) } } diff --git a/app/src/main/res/drawable/ic_action_skip.xml b/app/src/main/res/drawable/ic_action_skip.xml new file mode 100644 index 000000000..efe97da86 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_skip.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/layout/item_download.xml b/app/src/main/res/layout/item_download.xml index 2f6c56686..7743b2c19 100644 --- a/app/src/main/res/layout/item_download.xml +++ b/app/src/main/res/layout/item_download.xml @@ -154,7 +154,7 @@ android:visibility="gone" app:layout_constraintEnd_toStartOf="@id/button_resume" app:layout_constraintTop_toBottomOf="@id/recyclerView_chapters" - tools:visibility="visible" /> + tools:visibility="gone" />