diff --git a/app/build.gradle b/app/build.gradle index 8b8940a85..d66ea2886 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,6 +8,8 @@ plugins { id 'dagger.hilt.android.plugin' id 'androidx.room' id 'org.jetbrains.kotlin.plugin.serialization' + // enable if needed + // id 'dev.reformator.stacktracedecoroutinator' } android { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt index 5b28e6efc..05025c682 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/os/AppShortcutManager.kt @@ -173,6 +173,7 @@ class AppShortcutManager @Inject constructor( coil.execute( ImageRequest.Builder(context) .data(source.faviconUri()) + .mangaSourceExtra(source) .size(iconSize) .scale(Scale.FIT) .build(), diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index 25e2daa2a..0deb4c02a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -6,6 +6,7 @@ import android.database.sqlite.SQLiteFullException import androidx.annotation.DrawableRes import coil3.network.HttpException import com.davemorrissey.labs.subscaleview.decoder.ImageDecodeException +import kotlinx.coroutines.CancellationException import okhttp3.Response import okhttp3.internal.http2.StreamResetException import okio.FileNotFoundException @@ -64,6 +65,7 @@ fun Throwable.getDisplayMessage(resources: Resources): String = getDisplayMessag ?: resources.getString(R.string.error_occurred) private fun Throwable.getDisplayMessageOrNull(resources: Resources): String? = when (this) { + is CancellationException -> cause?.getDisplayMessageOrNull(resources) ?: message is CaughtException -> cause.getDisplayMessageOrNull(resources) is WrapperIOException -> cause.getDisplayMessageOrNull(resources) is ScrobblerAuthRequiredException -> resources.getString( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt index ef8f7f4ae..aac9037a0 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/details/domain/DetailsLoadUseCase.kt @@ -9,11 +9,9 @@ import androidx.core.text.parseAsHtml import coil3.request.CachePolicy import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async -import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.FlowCollector -import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn @@ -66,7 +64,6 @@ class DetailsLoadUseCase @Inject constructor( loadRemote(manga, override, force) } }.distinctUntilChanged() - .buffer(Channel.UNLIMITED) .flowOn(Dispatchers.Default) /** diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt index a6441bc33..8f0146c3e 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/ReaderViewModel.kt @@ -1,7 +1,6 @@ package org.koitharu.kotatsu.reader.ui import android.net.Uri -import android.util.Log import androidx.annotation.AnyThread import androidx.annotation.MainThread import androidx.annotation.WorkerThread @@ -396,7 +395,8 @@ class ReaderViewModel @Inject constructor( private fun loadImpl() { loadingJob = launchLoadingJob(Dispatchers.Default + EventExceptionHandler(onLoadingError)) { - val exception = try { + var exception: Exception? = null + try { detailsLoadUseCase(intent, force = false) .collect { details -> if (mangaDetails.value == null) { @@ -417,7 +417,13 @@ class ReaderViewModel @Inject constructor( val branch = chaptersLoader.peekChapter(newState.chapterId)?.branch selectedBranch.value = branch readerMode.value = mode - chaptersLoader.loadSingleChapter(newState.chapterId) + try { + chaptersLoader.loadSingleChapter(newState.chapterId) + } catch (e: Exception) { + readingState.value = null // try next time + exception = e.mergeWith(exception) + return@collect + } } mangaDetails.value = details.filterChapters(selectedBranch.value) @@ -431,19 +437,18 @@ class ReaderViewModel @Inject constructor( notifyStateChanged() content.value = ReaderContent(chaptersLoader.snapshot(), readingState.value) } - null // no errors } catch (e: CancellationException) { throw e } catch (e: Exception) { - e + exception = e.mergeWith(exception) } if (readingState.value == null) { onLoadingError.call( exception ?: IllegalStateException("Unable to load manga. This should never happen. Please report"), ) - } else if (exception != null) { + } else exception?.let { e -> // manga has been loaded but error occurred - errorEvent.call(exception) + errorEvent.call(e) } } } @@ -576,4 +581,11 @@ class ReaderViewModel @Inject constructor( val preferredBranch = requestedBranch ?: manga.getPreferredBranch(null) return ReaderState(manga, preferredBranch) } + + private fun Exception.mergeWith(other: Exception?): Exception = if (other == null) { + this + } else { + other.addSuppressed(this) + other + } } diff --git a/build.gradle b/build.gradle index 1b6586e7e..809d5a731 100644 --- a/build.gradle +++ b/build.gradle @@ -5,4 +5,5 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.room) apply false alias(libs.plugins.kotlinx.serizliation) apply false +// alias(libs.plugins.decoroutinator) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a0bdb353c..c57cc3188 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ constraintlayout = "2.2.1" coreKtx = "1.16.0" coroutines = "1.10.2" dagger = "2.56.2" -decoroutinator = "2.5.4" +decoroutinator = "2.5.5" desugar = "2.1.5" diskLruCache = "1.5" documentfile = "1.1.0"