mirror of
https://github.com/mihonapp/mihon.git
synced 2026-10-16 13:48:09 -07:00
Use an app scoped CoroutineScope
Replaces the eight ad hoc application lifetime scopes and the GlobalScope launchUI/launchIO/launchNow helpers with a single injected CoroutineScope(SupervisorJob() + Dispatchers.IO), qualified by @AppCoroutineScope. Every scope this replaces was already on IO, or named IO explicitly, so call sites keep their dispatcher. The two that need the main thread use scope.launchUI. Scopes with a real cancellation point keep their own: HttpPageLoader, ShizukuInstaller and the reader viewers. Only HttpPageLoader's page list cache save moves to the app scope, since it runs after recycle() has cancelled the loader's own scope. NotificationReceiver now holds the broadcast open with goAsync() while its work runs, so the process isn't torn down mid update. This reverts the revert of #3403. It was reverted for #3429, where restore() dropped every download because SourceManager.get() returned null until the extensions had loaded. That surface suspends since #3869, so the race is gone.
This commit is contained in:
@@ -13,7 +13,6 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import coil3.ImageLoader
|
||||
import coil3.SingletonImageLoader
|
||||
import coil3.memory.MemoryCache
|
||||
@@ -46,6 +45,7 @@ import eu.kanade.tachiyomi.util.system.animatorDurationScale
|
||||
import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.isDebugBuildType
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
@@ -54,6 +54,7 @@ import logcat.LogPriority
|
||||
import logcat.LogcatLogger
|
||||
import mihon.app.di.AppGraph
|
||||
import mihon.app.di.injekt.MetroInjektRegistrar
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import mihon.core.metro.GraphProvider
|
||||
import mihon.core.migration.Migration
|
||||
import mihon.core.migration.Migrator
|
||||
@@ -97,6 +98,9 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
||||
|
||||
@Inject private lateinit var migrations: Set<Migration>
|
||||
|
||||
@Inject @AppCoroutineScope
|
||||
private lateinit var scope: CoroutineScope
|
||||
|
||||
private val disableIncognitoReceiver = DisableIncognitoReceiver()
|
||||
|
||||
@SuppressLint("LaunchActivityFromNotification")
|
||||
@@ -126,8 +130,6 @@ class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.Factor
|
||||
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
|
||||
val scope = ProcessLifecycleOwner.get().lifecycleScope
|
||||
|
||||
// Show notification to disable Incognito Mode when it's enabled
|
||||
basePreferences.incognitoMode.changes()
|
||||
.onEach { enabled ->
|
||||
|
||||
@@ -11,7 +11,6 @@ import dev.zacsweers.metro.SingleIn
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
@@ -41,6 +40,7 @@ import kotlinx.serialization.encoding.Decoder
|
||||
import kotlinx.serialization.encoding.Encoder
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.core.common.storage.extension
|
||||
import tachiyomi.core.common.storage.nameWithoutExtension
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
@@ -65,14 +65,13 @@ import kotlin.time.Duration.Companion.seconds
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class DownloadCache(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val provider: DownloadProvider,
|
||||
private val sourceManager: SourceManager,
|
||||
private val storageManager: StorageManager,
|
||||
) {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
|
||||
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
|
||||
val changes = _changes.receiveAsFlow()
|
||||
.onStart { emit(Unit) }
|
||||
|
||||
@@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.drop
|
||||
@@ -18,6 +19,7 @@ import kotlinx.coroutines.flow.merge
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.core.common.storage.extension
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
@@ -40,6 +42,7 @@ import tachiyomi.i18n.MR
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class DownloadManager(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val provider: DownloadProvider,
|
||||
private val cache: DownloadCache,
|
||||
@@ -253,7 +256,7 @@ class DownloadManager(
|
||||
* @param source the source of the chapters.
|
||||
*/
|
||||
fun deleteChapters(chapters: List<Chapter>, manga: Manga, source: Source) {
|
||||
launchIO {
|
||||
scope.launchIO {
|
||||
val filteredChapters = getChaptersToDelete(chapters, manga)
|
||||
if (filteredChapters.isEmpty()) {
|
||||
return@launchIO
|
||||
@@ -280,7 +283,7 @@ class DownloadManager(
|
||||
* @param removeQueued whether to also remove queued downloads.
|
||||
*/
|
||||
fun deleteManga(manga: Manga, source: Source, removeQueued: Boolean = true) {
|
||||
launchIO {
|
||||
scope.launchIO {
|
||||
if (removeQueued) {
|
||||
downloader.removeFromQueue(manga)
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -47,11 +45,11 @@ import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.withContext
|
||||
import logcat.LogPriority
|
||||
import mihon.core.archive.ZipWriter
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
import okhttp3.Response
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNow
|
||||
import tachiyomi.core.common.util.system.ImageUtil
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.core.metadata.comicinfo.COMIC_INFO_FILE
|
||||
@@ -76,6 +74,7 @@ import kotlin.time.Duration.Companion.seconds
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class Downloader(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val provider: DownloadProvider,
|
||||
private val cache: DownloadCache,
|
||||
@@ -94,8 +93,6 @@ class Downloader(
|
||||
private val _queueState = MutableStateFlow<List<Download>>(emptyList())
|
||||
val queueState = _queueState.asStateFlow()
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
// Kept when canceled, so the next job can wait for it to finish
|
||||
private var downloaderJob: Job? = null
|
||||
|
||||
@@ -112,9 +109,8 @@ class Downloader(
|
||||
var isPaused: Boolean = false
|
||||
|
||||
init {
|
||||
launchNow {
|
||||
val chapters = async { store.restore() }
|
||||
addAllToQueue(chapters.await())
|
||||
scope.launch {
|
||||
addAllToQueue(store.restore())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ import eu.kanade.tachiyomi.util.system.cancelNotification
|
||||
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||
import eu.kanade.tachiyomi.util.system.notify
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.i18n.pluralStringResource
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
@@ -44,6 +46,7 @@ import mihon.icons.materialsymbols.R as MaterialSymbolsR
|
||||
|
||||
@Inject
|
||||
class LibraryUpdateNotifier(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val securityPreferences: SecurityPreferences,
|
||||
private val sourceManager: SourceManager,
|
||||
@@ -213,7 +216,7 @@ class LibraryUpdateNotifier(
|
||||
|
||||
// Per-manga notification
|
||||
if (!securityPreferences.hideNotificationContent.get()) {
|
||||
launchUI {
|
||||
scope.launchUI {
|
||||
context.notify(
|
||||
updates.map { (manga, chapters) ->
|
||||
NotificationManagerCompat.NotificationWithIdAndTag(
|
||||
|
||||
@@ -18,10 +18,12 @@ import eu.kanade.tachiyomi.util.system.notificationManager
|
||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.system.workManager
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mihon.app.di.appGraph
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.core.common.Constants
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.domain.chapter.interactor.GetChapter
|
||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||
import tachiyomi.domain.chapter.model.Chapter
|
||||
@@ -52,6 +54,9 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
|
||||
@Inject private lateinit var sourceManager: SourceManager
|
||||
|
||||
@Inject @AppCoroutineScope
|
||||
private lateinit var scope: CoroutineScope
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
context.appGraph.inject(this)
|
||||
|
||||
@@ -189,7 +194,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
* @param mangaId id of manga
|
||||
*/
|
||||
private fun markAsRead(chapterUrls: Array<String>, mangaId: Long) {
|
||||
launchIO {
|
||||
launchAsync {
|
||||
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
||||
.map {
|
||||
if (downloadPreferences.removeAfterMarkedAsRead.get()) {
|
||||
@@ -214,13 +219,28 @@ class NotificationReceiver : BroadcastReceiver() {
|
||||
* @param mangaId id of manga
|
||||
*/
|
||||
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
||||
launchIO {
|
||||
val manga = getManga.await(mangaId) ?: return@launchIO
|
||||
launchAsync {
|
||||
val manga = getManga.await(mangaId) ?: return@launchAsync
|
||||
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
||||
downloadManager.downloadChapters(manga, chapters)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs [block] on the application scope while keeping the broadcast alive, so the process isn't torn down before
|
||||
* the work finishes.
|
||||
*/
|
||||
private fun launchAsync(block: suspend CoroutineScope.() -> Unit) {
|
||||
val pendingResult = goAsync()
|
||||
scope.launch {
|
||||
try {
|
||||
block()
|
||||
} finally {
|
||||
pendingResult.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NAME = "NotificationReceiver"
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -33,6 +32,7 @@ import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import mihon.domain.extension.interactor.UpdateExtensionStores
|
||||
import mihon.domain.extension.model.ExtensionStore
|
||||
import mihon.domain.extension.repository.ExtensionStoreRepository
|
||||
@@ -53,6 +53,7 @@ import java.util.Locale
|
||||
@Inject
|
||||
@SingleIn(AppScope::class)
|
||||
class ExtensionManager(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val preferences: SourcePreferences,
|
||||
private val trustExtension: TrustExtension,
|
||||
@@ -62,8 +63,6 @@ class ExtensionManager(
|
||||
private val extensionUpdateNotifier: ExtensionUpdateNotifier,
|
||||
) {
|
||||
|
||||
val scope = CoroutineScope(SupervisorJob())
|
||||
|
||||
private val initialized = CompletableDeferred<Unit>()
|
||||
|
||||
private val iconMap = mutableMapOf<String, Drawable>()
|
||||
@@ -96,7 +95,7 @@ class ExtensionManager(
|
||||
init {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
loadExtensions()
|
||||
ExtensionInstallReceiver(InstallationListener()).register(context)
|
||||
ExtensionInstallReceiver(InstallationListener(), scope).register(context)
|
||||
|
||||
// Everything the load decision rests on can change while running, so decide again
|
||||
merge(
|
||||
|
||||
@@ -9,7 +9,6 @@ import androidx.core.net.toUri
|
||||
import eu.kanade.tachiyomi.BuildConfig
|
||||
import eu.kanade.tachiyomi.extension.model.Extension
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
@@ -19,10 +18,12 @@ import tachiyomi.core.common.util.system.logcat
|
||||
* notifies the given [listener] when the package is an extension.
|
||||
*
|
||||
* @param listener The listener that should be notified of extension installation events.
|
||||
* @param scope The scope the listener is notified in.
|
||||
*/
|
||||
internal class ExtensionInstallReceiver(private val listener: Listener) : BroadcastReceiver() {
|
||||
|
||||
val scope = CoroutineScope(SupervisorJob())
|
||||
internal class ExtensionInstallReceiver(
|
||||
private val listener: Listener,
|
||||
private val scope: CoroutineScope,
|
||||
) : BroadcastReceiver() {
|
||||
|
||||
fun register(context: Context) {
|
||||
ContextCompat.registerReceiver(context, this, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
|
||||
|
||||
@@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -22,6 +21,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.onCompletion
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
@@ -34,12 +34,12 @@ import java.io.File
|
||||
*/
|
||||
@Inject
|
||||
class ExtensionInstaller(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val context: Context,
|
||||
basePreferences: BasePreferences,
|
||||
networkHelper: NetworkHelper,
|
||||
) {
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.IO)
|
||||
private val activeJobs = mutableMapOf<String, Job>()
|
||||
private val activeSteps = mutableMapOf<Long, MutableStateFlow<InstallStep>>()
|
||||
private val extensionInstaller = basePreferences.extensionInstaller
|
||||
|
||||
@@ -8,8 +8,6 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -17,6 +15,7 @@ import kotlinx.coroutines.flow.filterNotNull
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.domain.source.model.StubSource
|
||||
import tachiyomi.domain.source.repository.StubSourceRepository
|
||||
import tachiyomi.domain.source.service.SourceManager
|
||||
@@ -27,14 +26,13 @@ import java.util.concurrent.ConcurrentHashMap
|
||||
@SingleIn(AppScope::class)
|
||||
@ContributesBinding(AppScope::class)
|
||||
class AndroidSourceManager(
|
||||
@AppCoroutineScope private val scope: CoroutineScope,
|
||||
private val extensionManager: ExtensionManager,
|
||||
private val sourceRepository: StubSourceRepository,
|
||||
private val localSource: LocalSource,
|
||||
private val downloadManager: Lazy<DownloadManager>,
|
||||
) : SourceManager {
|
||||
|
||||
private val scope = CoroutineScope(Job() + Dispatchers.IO)
|
||||
|
||||
/**
|
||||
* Null until the extensions have loaded, so that nothing observes the empty seed value.
|
||||
*/
|
||||
|
||||
@@ -54,6 +54,7 @@ import eu.kanade.tachiyomi.util.lang.byteSize
|
||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -68,6 +69,7 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import logcat.LogPriority
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import tachiyomi.core.common.preference.toggle
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.launchNonCancellable
|
||||
@@ -98,6 +100,7 @@ import kotlin.time.Instant
|
||||
@AssistedInject
|
||||
class ReaderViewModel(
|
||||
@Assisted private val savedState: SavedStateHandle,
|
||||
@AppCoroutineScope private val appScope: CoroutineScope,
|
||||
private val context: Context,
|
||||
private val sourceManager: SourceManager,
|
||||
private val downloadManager: DownloadManager,
|
||||
@@ -332,7 +335,15 @@ class ReaderViewModel(
|
||||
mutableState.update { it.copy(manga = manga, source = source) }
|
||||
if (chapterId == -1L) chapterId = initialChapterId
|
||||
|
||||
loader = ChapterLoader(context, downloadManager, downloadProvider, chapterCache, manga, source)
|
||||
loader = ChapterLoader(
|
||||
context,
|
||||
downloadManager,
|
||||
downloadProvider,
|
||||
chapterCache,
|
||||
appScope,
|
||||
manga,
|
||||
source,
|
||||
)
|
||||
|
||||
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
||||
} catch (e: Throwable) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import mihon.core.archive.archiveReader
|
||||
import mihon.core.archive.epubReader
|
||||
import tachiyomi.core.common.i18n.stringResource
|
||||
@@ -27,6 +28,7 @@ class ChapterLoader(
|
||||
private val downloadManager: DownloadManager,
|
||||
private val downloadProvider: DownloadProvider,
|
||||
private val chapterCache: ChapterCache,
|
||||
private val appScope: CoroutineScope,
|
||||
private val manga: Manga,
|
||||
private val source: Source,
|
||||
) {
|
||||
@@ -102,7 +104,7 @@ class ChapterLoader(
|
||||
is Format.Epub -> EpubPageLoader(format.file.epubReader(context))
|
||||
}
|
||||
}
|
||||
source is HttpSource -> HttpPageLoader(chapter, source, chapterCache)
|
||||
source is HttpSource -> HttpPageLoader(chapter, source, chapterCache, appScope)
|
||||
source is StubSource -> error(context.stringResource(MR.strings.source_not_installed, source.toString()))
|
||||
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ internal class HttpPageLoader(
|
||||
private val chapter: ReaderChapter,
|
||||
private val source: HttpSource,
|
||||
private val chapterCache: ChapterCache,
|
||||
private val appScope: CoroutineScope,
|
||||
) : PageLoader() {
|
||||
|
||||
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
@@ -132,7 +133,7 @@ internal class HttpPageLoader(
|
||||
|
||||
// Cache current page list progress for online chapters to allow a faster reopen
|
||||
chapter.pages?.let { pages ->
|
||||
launchIO {
|
||||
appScope.launchIO {
|
||||
try {
|
||||
// Convert to pages without reader information
|
||||
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
|
||||
|
||||
@@ -4,8 +4,12 @@ import dev.zacsweers.metro.AppScope
|
||||
import dev.zacsweers.metro.BindingContainer
|
||||
import dev.zacsweers.metro.Provides
|
||||
import dev.zacsweers.metro.SingleIn
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
import mihon.core.metro.AppCoroutineScope
|
||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||
import nl.adaptivity.xmlutil.core.XmlVersion
|
||||
import nl.adaptivity.xmlutil.serialization.XML
|
||||
@@ -13,6 +17,11 @@ import nl.adaptivity.xmlutil.serialization.XML
|
||||
@BindingContainer
|
||||
object AppBindings {
|
||||
|
||||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
@AppCoroutineScope
|
||||
fun providesAppCoroutineScope(): CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
@Provides
|
||||
@SingleIn(AppScope::class)
|
||||
fun providesJson(): Json = Json {
|
||||
|
||||
Reference in New Issue
Block a user