Revert "Use app scoped CoroutineScope (#3403)"
This reverts commit 509eee5dfb.
Fixes #3429
This commit is contained in:
@@ -19,13 +19,13 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.flow.shareIn
|
import kotlinx.coroutines.flow.shareIn
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.coroutines.withTimeoutOrNull
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
@@ -63,16 +63,17 @@ import kotlin.time.Duration.Companion.seconds
|
|||||||
*/
|
*/
|
||||||
class DownloadCache(
|
class DownloadCache(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val provider: DownloadProvider = Injekt.get(),
|
private val provider: DownloadProvider = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val extensionManager: ExtensionManager = Injekt.get(),
|
private val extensionManager: ExtensionManager = Injekt.get(),
|
||||||
private val storageManager: StorageManager = Injekt.get(),
|
private val storageManager: StorageManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
|
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
|
||||||
val changes = _changes.receiveAsFlow()
|
val changes = _changes.receiveAsFlow()
|
||||||
.onStart { emit(Unit) }
|
.onStart { emit(Unit) }
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.shareIn(scope, SharingStarted.Lazily, 1)
|
.shareIn(scope, SharingStarted.Lazily, 1)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -100,7 +101,7 @@ class DownloadCache(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
// Attempt to read cache file
|
// Attempt to read cache file
|
||||||
scope.launchIO {
|
scope.launch {
|
||||||
rootDownloadsDirMutex.withLock {
|
rootDownloadsDirMutex.withLock {
|
||||||
try {
|
try {
|
||||||
if (diskCacheFile.exists()) {
|
if (diskCacheFile.exists()) {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import android.content.Context
|
|||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
@@ -36,7 +35,6 @@ import uy.kohesive.injekt.api.get
|
|||||||
*/
|
*/
|
||||||
class DownloadManager(
|
class DownloadManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val provider: DownloadProvider = Injekt.get(),
|
private val provider: DownloadProvider = Injekt.get(),
|
||||||
private val cache: DownloadCache = Injekt.get(),
|
private val cache: DownloadCache = Injekt.get(),
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
@@ -47,7 +45,7 @@ class DownloadManager(
|
|||||||
/**
|
/**
|
||||||
* Downloader whose only task is to download chapters.
|
* Downloader whose only task is to download chapters.
|
||||||
*/
|
*/
|
||||||
private val downloader = Downloader(context, provider, cache, scope)
|
private val downloader = Downloader(context, provider, cache)
|
||||||
|
|
||||||
val isRunning: Boolean
|
val isRunning: Boolean
|
||||||
get() = downloader.isRunning
|
get() = downloader.isRunning
|
||||||
@@ -223,7 +221,7 @@ class DownloadManager(
|
|||||||
* @param source the source of the chapters.
|
* @param source the source of the chapters.
|
||||||
*/
|
*/
|
||||||
fun deleteChapters(chapters: List<Chapter>, manga: Manga, source: Source) {
|
fun deleteChapters(chapters: List<Chapter>, manga: Manga, source: Source) {
|
||||||
scope.launchIO {
|
launchIO {
|
||||||
val filteredChapters = getChaptersToDelete(chapters, manga)
|
val filteredChapters = getChaptersToDelete(chapters, manga)
|
||||||
if (filteredChapters.isEmpty()) {
|
if (filteredChapters.isEmpty()) {
|
||||||
return@launchIO
|
return@launchIO
|
||||||
@@ -250,7 +248,7 @@ class DownloadManager(
|
|||||||
* @param removeQueued whether to also remove queued downloads.
|
* @param removeQueued whether to also remove queued downloads.
|
||||||
*/
|
*/
|
||||||
fun deleteManga(manga: Manga, source: Source, removeQueued: Boolean = true) {
|
fun deleteManga(manga: Manga, source: Source, removeQueued: Boolean = true) {
|
||||||
scope.launchIO {
|
launchIO {
|
||||||
if (removeQueued) {
|
if (removeQueued) {
|
||||||
downloader.removeFromQueue(manga)
|
downloader.removeFromQueue(manga)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import kotlinx.coroutines.CancellationException
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -43,6 +44,7 @@ import okhttp3.Response
|
|||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
import tachiyomi.core.common.storage.extension
|
import tachiyomi.core.common.storage.extension
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
|
import tachiyomi.core.common.util.lang.launchNow
|
||||||
import tachiyomi.core.common.util.lang.withIOContext
|
import tachiyomi.core.common.util.lang.withIOContext
|
||||||
import tachiyomi.core.common.util.system.ImageUtil
|
import tachiyomi.core.common.util.system.ImageUtil
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
@@ -70,7 +72,6 @@ class Downloader(
|
|||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val provider: DownloadProvider,
|
private val provider: DownloadProvider,
|
||||||
private val cache: DownloadCache,
|
private val cache: DownloadCache,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val chapterCache: ChapterCache = Injekt.get(),
|
private val chapterCache: ChapterCache = Injekt.get(),
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
@@ -95,6 +96,7 @@ class Downloader(
|
|||||||
*/
|
*/
|
||||||
private val notifier by lazy { DownloadNotifier(context) }
|
private val notifier by lazy { DownloadNotifier(context) }
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
private var downloaderJob: Job? = null
|
private var downloaderJob: Job? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,7 +112,7 @@ class Downloader(
|
|||||||
var isPaused: Boolean = false
|
var isPaused: Boolean = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launch {
|
launchNow {
|
||||||
val chapters = async { store.restore() }
|
val chapters = async { store.restore() }
|
||||||
addAllToQueue(chapters.await())
|
addAllToQueue(chapters.await())
|
||||||
}
|
}
|
||||||
@@ -189,7 +191,7 @@ class Downloader(
|
|||||||
private fun launchDownloaderJob() {
|
private fun launchDownloaderJob() {
|
||||||
if (isRunning) return
|
if (isRunning) return
|
||||||
|
|
||||||
downloaderJob = scope.launchIO {
|
downloaderJob = scope.launch {
|
||||||
val activeDownloadsFlow = combine(
|
val activeDownloadsFlow = combine(
|
||||||
queueState,
|
queueState,
|
||||||
downloadPreferences.parallelSourceLimit.changes(),
|
downloadPreferences.parallelSourceLimit.changes(),
|
||||||
|
|||||||
@@ -28,11 +28,10 @@ import eu.kanade.tachiyomi.util.system.cancelNotification
|
|||||||
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
import eu.kanade.tachiyomi.util.system.getBitmapOrNull
|
||||||
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
import eu.kanade.tachiyomi.util.system.notificationBuilder
|
||||||
import eu.kanade.tachiyomi.util.system.notify
|
import eu.kanade.tachiyomi.util.system.notify
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
import tachiyomi.core.common.i18n.pluralStringResource
|
import tachiyomi.core.common.i18n.pluralStringResource
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
|
import tachiyomi.core.common.util.lang.launchUI
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@@ -45,7 +44,6 @@ import java.text.NumberFormat
|
|||||||
|
|
||||||
class LibraryUpdateNotifier(
|
class LibraryUpdateNotifier(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope = Injekt.get(),
|
|
||||||
private val securityPreferences: SecurityPreferences = Injekt.get(),
|
private val securityPreferences: SecurityPreferences = Injekt.get(),
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
@@ -210,7 +208,7 @@ class LibraryUpdateNotifier(
|
|||||||
|
|
||||||
// Per-manga notification
|
// Per-manga notification
|
||||||
if (!securityPreferences.hideNotificationContent.get()) {
|
if (!securityPreferences.hideNotificationContent.get()) {
|
||||||
scope.launch {
|
launchUI {
|
||||||
context.notify(
|
context.notify(
|
||||||
updates.map { (manga, chapters) ->
|
updates.map { (manga, chapters) ->
|
||||||
NotificationManagerCompat.NotificationWithIdAndTag(
|
NotificationManagerCompat.NotificationWithIdAndTag(
|
||||||
|
|||||||
@@ -17,12 +17,9 @@ import eu.kanade.tachiyomi.util.system.getParcelableExtraCompat
|
|||||||
import eu.kanade.tachiyomi.util.system.notificationManager
|
import eu.kanade.tachiyomi.util.system.notificationManager
|
||||||
import eu.kanade.tachiyomi.util.system.toShareIntent
|
import eu.kanade.tachiyomi.util.system.toShareIntent
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.CoroutineStart
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import tachiyomi.core.common.Constants
|
import tachiyomi.core.common.Constants
|
||||||
|
import tachiyomi.core.common.util.lang.launchIO
|
||||||
import tachiyomi.domain.chapter.interactor.GetChapter
|
import tachiyomi.domain.chapter.interactor.GetChapter
|
||||||
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
import tachiyomi.domain.chapter.interactor.UpdateChapter
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
@@ -35,8 +32,6 @@ import tachiyomi.i18n.MR
|
|||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
import kotlin.coroutines.EmptyCoroutineContext
|
|
||||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,7 +45,6 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
private val getChapter: GetChapter by injectLazy()
|
private val getChapter: GetChapter by injectLazy()
|
||||||
private val updateChapter: UpdateChapter by injectLazy()
|
private val updateChapter: UpdateChapter by injectLazy()
|
||||||
private val downloadManager: DownloadManager by injectLazy()
|
private val downloadManager: DownloadManager by injectLazy()
|
||||||
private val scope: CoroutineScope by injectLazy()
|
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
when (intent.action) {
|
when (intent.action) {
|
||||||
@@ -203,7 +197,7 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
val downloadPreferences: DownloadPreferences = Injekt.get()
|
val downloadPreferences: DownloadPreferences = Injekt.get()
|
||||||
val sourceManager: SourceManager = Injekt.get()
|
val sourceManager: SourceManager = Injekt.get()
|
||||||
|
|
||||||
async(scope, Dispatchers.IO) {
|
launchIO {
|
||||||
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
val toUpdate = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
||||||
.map {
|
.map {
|
||||||
val chapter = it.copy(read = true)
|
val chapter = it.copy(read = true)
|
||||||
@@ -229,27 +223,13 @@ class NotificationReceiver : BroadcastReceiver() {
|
|||||||
* @param mangaId id of manga
|
* @param mangaId id of manga
|
||||||
*/
|
*/
|
||||||
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
private fun downloadChapters(chapterUrls: Array<String>, mangaId: Long) {
|
||||||
async(scope, Dispatchers.IO) {
|
launchIO {
|
||||||
val manga = getManga.await(mangaId) ?: return@async
|
val manga = getManga.await(mangaId) ?: return@launchIO
|
||||||
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
val chapters = chapterUrls.mapNotNull { getChapter.await(it, mangaId) }
|
||||||
downloadManager.downloadChapters(manga, chapters)
|
downloadManager.downloadChapters(manga, chapters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun BroadcastReceiver.async(
|
|
||||||
scope: CoroutineScope,
|
|
||||||
context: CoroutineContext = EmptyCoroutineContext,
|
|
||||||
start: CoroutineStart = CoroutineStart.DEFAULT,
|
|
||||||
block: suspend CoroutineScope.() -> Unit,
|
|
||||||
) {
|
|
||||||
val result = goAsync()
|
|
||||||
try {
|
|
||||||
scope.launch(context, start, block)
|
|
||||||
} finally {
|
|
||||||
result.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val NAME = "NotificationReceiver"
|
private const val NAME = "NotificationReceiver"
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package eu.kanade.tachiyomi.di
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.lifecycle.ProcessLifecycleOwner
|
|
||||||
import androidx.lifecycle.lifecycleScope
|
|
||||||
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
import androidx.sqlite.driver.bundled.BundledSQLiteDriver
|
||||||
import app.cash.sqldelight.db.SqlDriver
|
import app.cash.sqldelight.db.SqlDriver
|
||||||
import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteConfiguration
|
import com.eygraber.sqldelight.androidx.driver.AndroidxSqliteConfiguration
|
||||||
@@ -22,9 +20,6 @@ import eu.kanade.tachiyomi.extension.ExtensionManager
|
|||||||
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
import eu.kanade.tachiyomi.network.JavaScriptEngine
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
import eu.kanade.tachiyomi.source.AndroidSourceManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.SupervisorJob
|
|
||||||
import kotlinx.coroutines.plus
|
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.protobuf.ProtoBuf
|
import kotlinx.serialization.protobuf.ProtoBuf
|
||||||
import nl.adaptivity.xmlutil.XmlDeclMode
|
import nl.adaptivity.xmlutil.XmlDeclMode
|
||||||
@@ -112,20 +107,18 @@ class AppModule(val app: Application) : InjektModule {
|
|||||||
ProtoBuf
|
ProtoBuf
|
||||||
}
|
}
|
||||||
|
|
||||||
addSingletonFactory<CoroutineScope> { ProcessLifecycleOwner.get().lifecycleScope + SupervisorJob() }
|
|
||||||
|
|
||||||
addSingletonFactory { ChapterCache(app, get()) }
|
addSingletonFactory { ChapterCache(app, get()) }
|
||||||
addSingletonFactory { CoverCache(app) }
|
addSingletonFactory { CoverCache(app) }
|
||||||
|
|
||||||
addSingletonFactory { NetworkHelper(app, get(), get()) }
|
addSingletonFactory { NetworkHelper(app, get()) }
|
||||||
addSingletonFactory { JavaScriptEngine(app) }
|
addSingletonFactory { JavaScriptEngine(app) }
|
||||||
|
|
||||||
addSingletonFactory<SourceManager> { AndroidSourceManager(app, get(), get(), get()) }
|
addSingletonFactory<SourceManager> { AndroidSourceManager(app, get(), get()) }
|
||||||
addSingletonFactory { ExtensionManager(app, get()) }
|
addSingletonFactory { ExtensionManager(app) }
|
||||||
|
|
||||||
addSingletonFactory { DownloadProvider(app) }
|
addSingletonFactory { DownloadProvider(app) }
|
||||||
addSingletonFactory { DownloadManager(app, get()) }
|
addSingletonFactory { DownloadManager(app) }
|
||||||
addSingletonFactory { DownloadCache(app, get()) }
|
addSingletonFactory { DownloadCache(app) }
|
||||||
|
|
||||||
addSingletonFactory { TrackerManager() }
|
addSingletonFactory { TrackerManager() }
|
||||||
addSingletonFactory { DelayedTrackingStore(app) }
|
addSingletonFactory { DelayedTrackingStore(app) }
|
||||||
@@ -135,7 +128,7 @@ class AppModule(val app: Application) : InjektModule {
|
|||||||
addSingletonFactory { AndroidStorageFolderProvider(app) }
|
addSingletonFactory { AndroidStorageFolderProvider(app) }
|
||||||
addSingletonFactory { LocalSourceFileSystem(get()) }
|
addSingletonFactory { LocalSourceFileSystem(get()) }
|
||||||
addSingletonFactory { LocalCoverManager(app, get()) }
|
addSingletonFactory { LocalCoverManager(app, get()) }
|
||||||
addSingletonFactory { StorageManager(app, get(), get()) }
|
addSingletonFactory { StorageManager(app, get()) }
|
||||||
|
|
||||||
// Asynchronously init expensive components for a faster cold start
|
// Asynchronously init expensive components for a faster cold start
|
||||||
ContextCompat.getMainExecutor(app).execute {
|
ContextCompat.getMainExecutor(app).execute {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
|
|||||||
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
import eu.kanade.tachiyomi.extension.util.ExtensionLoader
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -40,10 +41,12 @@ import java.util.Locale
|
|||||||
*/
|
*/
|
||||||
class ExtensionManager(
|
class ExtensionManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val preferences: SourcePreferences = Injekt.get(),
|
private val preferences: SourcePreferences = Injekt.get(),
|
||||||
private val trustExtension: TrustExtension = Injekt.get(),
|
private val trustExtension: TrustExtension = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val scope = CoroutineScope(SupervisorJob())
|
||||||
|
|
||||||
private val _isInitialized = MutableStateFlow(false)
|
private val _isInitialized = MutableStateFlow(false)
|
||||||
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
val isInitialized: StateFlow<Boolean> = _isInitialized.asStateFlow()
|
||||||
|
|
||||||
@@ -55,7 +58,7 @@ class ExtensionManager(
|
|||||||
/**
|
/**
|
||||||
* The installer which installs, updates and uninstalls the extensions.
|
* The installer which installs, updates and uninstalls the extensions.
|
||||||
*/
|
*/
|
||||||
private val installer by lazy { ExtensionInstaller(context, scope) }
|
private val installer by lazy { ExtensionInstaller(context) }
|
||||||
|
|
||||||
private val iconMap = mutableMapOf<String, Drawable>()
|
private val iconMap = mutableMapOf<String, Drawable>()
|
||||||
|
|
||||||
|
|||||||
@@ -12,15 +12,16 @@ import eu.kanade.tachiyomi.network.NetworkHelper
|
|||||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.onCompletion
|
import kotlinx.coroutines.flow.onCompletion
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
|
||||||
import tachiyomi.core.common.util.system.logcat
|
import tachiyomi.core.common.util.system.logcat
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@@ -33,8 +34,9 @@ import java.io.File
|
|||||||
*/
|
*/
|
||||||
internal class ExtensionInstaller(
|
internal class ExtensionInstaller(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
private val activeJobs = mutableMapOf<String, Job>()
|
private val activeJobs = mutableMapOf<String, Job>()
|
||||||
private val activeSteps = mutableMapOf<Long, MutableStateFlow<InstallStep>>()
|
private val activeSteps = mutableMapOf<Long, MutableStateFlow<InstallStep>>()
|
||||||
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller
|
private val extensionInstaller = Injekt.get<BasePreferences>().extensionInstaller
|
||||||
@@ -55,7 +57,7 @@ internal class ExtensionInstaller(
|
|||||||
val step = MutableStateFlow(InstallStep.Pending)
|
val step = MutableStateFlow(InstallStep.Pending)
|
||||||
activeSteps[downloadId] = step
|
activeSteps[downloadId] = step
|
||||||
|
|
||||||
val job = scope.launchIO {
|
val job = scope.launch {
|
||||||
val tmpFile = File(context.cacheDir, "extension_${extension.pkgName}.apk")
|
val tmpFile = File(context.cacheDir, "extension_${extension.pkgName}.apk")
|
||||||
try {
|
try {
|
||||||
step.value = InstallStep.Downloading
|
step.value = InstallStep.Downloading
|
||||||
|
|||||||
@@ -5,14 +5,16 @@ import eu.kanade.tachiyomi.data.download.DownloadManager
|
|||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import tachiyomi.core.common.util.lang.launchIO
|
|
||||||
import tachiyomi.domain.source.model.StubSource
|
import tachiyomi.domain.source.model.StubSource
|
||||||
import tachiyomi.domain.source.repository.StubSourceRepository
|
import tachiyomi.domain.source.repository.StubSourceRepository
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
@@ -24,7 +26,6 @@ import java.util.concurrent.ConcurrentHashMap
|
|||||||
|
|
||||||
class AndroidSourceManager(
|
class AndroidSourceManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val extensionManager: ExtensionManager,
|
private val extensionManager: ExtensionManager,
|
||||||
private val sourceRepository: StubSourceRepository,
|
private val sourceRepository: StubSourceRepository,
|
||||||
) : SourceManager {
|
) : SourceManager {
|
||||||
@@ -34,6 +35,8 @@ class AndroidSourceManager(
|
|||||||
|
|
||||||
private val downloadManager: DownloadManager by injectLazy()
|
private val downloadManager: DownloadManager by injectLazy()
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Job() + Dispatchers.IO)
|
||||||
|
|
||||||
private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap<Long, Source>())
|
private val sourcesMapFlow = MutableStateFlow(ConcurrentHashMap<Long, Source>())
|
||||||
|
|
||||||
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
private val stubSourcesMap = ConcurrentHashMap<Long, StubSource>()
|
||||||
@@ -41,7 +44,7 @@ class AndroidSourceManager(
|
|||||||
override val sources: Flow<List<Source>> = sourcesMapFlow.map { it.values.toList() }
|
override val sources: Flow<List<Source>> = sourcesMapFlow.map { it.values.toList() }
|
||||||
|
|
||||||
init {
|
init {
|
||||||
scope.launchIO {
|
scope.launch {
|
||||||
extensionManager.installedExtensionsFlow
|
extensionManager.installedExtensionsFlow
|
||||||
.collectLatest { extensions ->
|
.collectLatest { extensions ->
|
||||||
val mutableMap = ConcurrentHashMap<Long, Source>(
|
val mutableMap = ConcurrentHashMap<Long, Source>(
|
||||||
@@ -64,7 +67,7 @@ class AndroidSourceManager(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.launchIO {
|
scope.launch {
|
||||||
sourceRepository.subscribeAll()
|
sourceRepository.subscribeAll()
|
||||||
.collectLatest { sources ->
|
.collectLatest { sources ->
|
||||||
val mutableMap = stubSourcesMap.toMutableMap()
|
val mutableMap = stubSourcesMap.toMutableMap()
|
||||||
@@ -95,9 +98,9 @@ class AndroidSourceManager(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun registerStubSource(source: StubSource) {
|
private fun registerStubSource(source: StubSource) {
|
||||||
scope.launchIO {
|
scope.launch {
|
||||||
val dbSource = sourceRepository.getStubSource(source.id)
|
val dbSource = sourceRepository.getStubSource(source.id)
|
||||||
if (dbSource == source) return@launchIO
|
if (dbSource == source) return@launch
|
||||||
sourceRepository.upsertStubSource(source.id, source.lang, source.name)
|
sourceRepository.upsertStubSource(source.id, source.lang, source.name)
|
||||||
if (dbSource != null) {
|
if (dbSource != null) {
|
||||||
downloadManager.renameSource(dbSource, source)
|
downloadManager.renameSource(dbSource, source)
|
||||||
|
|||||||
@@ -286,7 +286,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
|
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
val source = sourceManager.getOrStub(manga.source)
|
val source = sourceManager.getOrStub(manga.source)
|
||||||
loader = ChapterLoader(context, viewModelScope, downloadManager, downloadProvider, manga, source)
|
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source)
|
||||||
|
|
||||||
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
loadChapter(loader!!, chapterList.first { chapterId == it.chapter.id })
|
||||||
Result.success(true)
|
Result.success(true)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import eu.kanade.tachiyomi.data.download.DownloadProvider
|
|||||||
import eu.kanade.tachiyomi.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import mihon.core.archive.archiveReader
|
import mihon.core.archive.archiveReader
|
||||||
import mihon.core.archive.epubReader
|
import mihon.core.archive.epubReader
|
||||||
import tachiyomi.core.common.i18n.stringResource
|
import tachiyomi.core.common.i18n.stringResource
|
||||||
@@ -23,7 +22,6 @@ import tachiyomi.source.local.io.Format
|
|||||||
*/
|
*/
|
||||||
class ChapterLoader(
|
class ChapterLoader(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val downloadManager: DownloadManager,
|
private val downloadManager: DownloadManager,
|
||||||
private val downloadProvider: DownloadProvider,
|
private val downloadProvider: DownloadProvider,
|
||||||
private val manga: Manga,
|
private val manga: Manga,
|
||||||
@@ -102,7 +100,7 @@ class ChapterLoader(
|
|||||||
is Format.Epub -> EpubPageLoader(format.file.epubReader(context))
|
is Format.Epub -> EpubPageLoader(format.file.epubReader(context))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
source is HttpSource -> HttpPageLoader(chapter, source, scope)
|
source is HttpSource -> HttpPageLoader(chapter, source)
|
||||||
source is StubSource -> error(context.stringResource(MR.strings.source_not_installed, source.toString()))
|
source is StubSource -> error(context.stringResource(MR.strings.source_not_installed, source.toString()))
|
||||||
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
|
else -> error(context.stringResource(MR.strings.loader_not_implemented_error))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,9 @@ import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
|
|||||||
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.SupervisorJob
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.runInterruptible
|
import kotlinx.coroutines.runInterruptible
|
||||||
@@ -30,10 +31,11 @@ import kotlin.math.min
|
|||||||
internal class HttpPageLoader(
|
internal class HttpPageLoader(
|
||||||
private val chapter: ReaderChapter,
|
private val chapter: ReaderChapter,
|
||||||
private val source: HttpSource,
|
private val source: HttpSource,
|
||||||
scope: CoroutineScope,
|
|
||||||
private val chapterCache: ChapterCache = Injekt.get(),
|
private val chapterCache: ChapterCache = Injekt.get(),
|
||||||
) : PageLoader() {
|
) : PageLoader() {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A queue used to manage requests one by one while allowing priorities.
|
* A queue used to manage requests one by one while allowing priorities.
|
||||||
*/
|
*/
|
||||||
@@ -122,14 +124,14 @@ internal class HttpPageLoader(
|
|||||||
queue.offer(PriorityPage(page, PriorityPage.RETRY))
|
queue.offer(PriorityPage(page, PriorityPage.RETRY))
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
|
||||||
override fun recycle() {
|
override fun recycle() {
|
||||||
super.recycle()
|
super.recycle()
|
||||||
|
scope.cancel()
|
||||||
queue.clear()
|
queue.clear()
|
||||||
|
|
||||||
// Cache current page list progress for online chapters to allow a faster reopen
|
// Cache current page list progress for online chapters to allow a faster reopen
|
||||||
chapter.pages?.let { pages ->
|
chapter.pages?.let { pages ->
|
||||||
GlobalScope.launchIO {
|
launchIO {
|
||||||
try {
|
try {
|
||||||
// Convert to pages without reader information
|
// Convert to pages without reader information
|
||||||
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
|
val pagesToSave = pages.map { Page(it.index, it.url, it.imageUrl) }
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import eu.kanade.tachiyomi.network.interceptor.CloudflareInterceptor
|
|||||||
import eu.kanade.tachiyomi.network.interceptor.IgnoreGzipInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.IgnoreGzipInterceptor
|
||||||
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.UncaughtExceptionInterceptor
|
||||||
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
|
import eu.kanade.tachiyomi.network.interceptor.UserAgentInterceptor
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import okhttp3.Cache
|
import okhttp3.Cache
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.brotli.BrotliInterceptor
|
import okhttp3.brotli.BrotliInterceptor
|
||||||
@@ -16,7 +15,6 @@ import java.util.concurrent.TimeUnit
|
|||||||
class NetworkHelper(
|
class NetworkHelper(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val preferences: NetworkPreferences,
|
private val preferences: NetworkPreferences,
|
||||||
scope: CoroutineScope,
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val cookieJar = AndroidCookieJar()
|
val cookieJar = AndroidCookieJar()
|
||||||
@@ -64,7 +62,7 @@ class NetworkHelper(
|
|||||||
|
|
||||||
val client = clientBuilder
|
val client = clientBuilder
|
||||||
.addInterceptor(
|
.addInterceptor(
|
||||||
CloudflareInterceptor(context, cookieJar, scope, ::defaultUserAgentProvider),
|
CloudflareInterceptor(context, cookieJar, ::defaultUserAgentProvider),
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
|
|||||||
+1
-3
@@ -11,7 +11,6 @@ import androidx.core.content.ContextCompat
|
|||||||
import eu.kanade.tachiyomi.network.AndroidCookieJar
|
import eu.kanade.tachiyomi.network.AndroidCookieJar
|
||||||
import eu.kanade.tachiyomi.util.system.isOutdated
|
import eu.kanade.tachiyomi.util.system.isOutdated
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import okhttp3.Cookie
|
import okhttp3.Cookie
|
||||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
@@ -26,9 +25,8 @@ import java.util.concurrent.CountDownLatch
|
|||||||
class CloudflareInterceptor(
|
class CloudflareInterceptor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val cookieManager: AndroidCookieJar,
|
private val cookieManager: AndroidCookieJar,
|
||||||
scope: CoroutineScope,
|
|
||||||
defaultUserAgentProvider: () -> String,
|
defaultUserAgentProvider: () -> String,
|
||||||
) : WebViewInterceptor(context, scope, defaultUserAgentProvider) {
|
) : WebViewInterceptor(context, defaultUserAgentProvider) {
|
||||||
|
|
||||||
private val executor = ContextCompat.getMainExecutor(context)
|
private val executor = ContextCompat.getMainExecutor(context)
|
||||||
|
|
||||||
|
|||||||
+2
-4
@@ -9,12 +9,11 @@ import eu.kanade.tachiyomi.util.system.DeviceUtil
|
|||||||
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
import eu.kanade.tachiyomi.util.system.WebViewUtil
|
||||||
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
||||||
import eu.kanade.tachiyomi.util.system.toast
|
import eu.kanade.tachiyomi.util.system.toast
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import okhttp3.Headers
|
import okhttp3.Headers
|
||||||
import okhttp3.Interceptor
|
import okhttp3.Interceptor
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
|
import tachiyomi.core.common.util.lang.launchUI
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
@@ -22,7 +21,6 @@ import java.util.concurrent.TimeUnit
|
|||||||
|
|
||||||
abstract class WebViewInterceptor(
|
abstract class WebViewInterceptor(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
private val scope: CoroutineScope,
|
|
||||||
private val defaultUserAgentProvider: () -> String,
|
private val defaultUserAgentProvider: () -> String,
|
||||||
) : Interceptor {
|
) : Interceptor {
|
||||||
|
|
||||||
@@ -58,7 +56,7 @@ abstract class WebViewInterceptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!WebViewUtil.supportsWebView(context)) {
|
if (!WebViewUtil.supportsWebView(context)) {
|
||||||
scope.launch {
|
launchUI {
|
||||||
context.toast(MR.strings.information_webview_required, Toast.LENGTH_LONG)
|
context.toast(MR.strings.information_webview_required, Toast.LENGTH_LONG)
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -1,12 +1,48 @@
|
|||||||
package tachiyomi.core.common.util.lang
|
package tachiyomi.core.common.util.lang
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.CoroutineStart
|
||||||
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
|
||||||
|
*
|
||||||
|
* **Possible replacements**
|
||||||
|
* - suspend function
|
||||||
|
* - custom scope like view or presenter scope
|
||||||
|
*/
|
||||||
|
@DelicateCoroutinesApi
|
||||||
|
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
|
||||||
|
*
|
||||||
|
* **Possible replacements**
|
||||||
|
* - suspend function
|
||||||
|
* - custom scope like view or presenter scope
|
||||||
|
*/
|
||||||
|
@DelicateCoroutinesApi
|
||||||
|
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT, block)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
|
||||||
|
*
|
||||||
|
* **Possible replacements**
|
||||||
|
* - suspend function
|
||||||
|
* - custom scope like view or presenter scope
|
||||||
|
*/
|
||||||
|
@DelicateCoroutinesApi
|
||||||
|
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
|
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)
|
||||||
|
|
||||||
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
|
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
|
||||||
launch(Dispatchers.Main, block = block)
|
launch(Dispatchers.Main, block = block)
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import kotlinx.coroutines.channels.Channel
|
|||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.drop
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
@@ -18,14 +17,15 @@ import kotlinx.coroutines.flow.shareIn
|
|||||||
|
|
||||||
class StorageManager(
|
class StorageManager(
|
||||||
private val context: Context,
|
private val context: Context,
|
||||||
scope: CoroutineScope,
|
|
||||||
storagePreferences: StoragePreferences,
|
storagePreferences: StoragePreferences,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
private val scope = CoroutineScope(Dispatchers.IO)
|
||||||
|
|
||||||
private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory.get())
|
private var baseDir: UniFile? = getBaseDir(storagePreferences.baseStorageDirectory.get())
|
||||||
|
|
||||||
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
|
private val _changes: Channel<Unit> = Channel(Channel.UNLIMITED)
|
||||||
val changes = _changes.receiveAsFlow()
|
val changes = _changes.receiveAsFlow()
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.shareIn(scope, SharingStarted.Lazily, 1)
|
.shareIn(scope, SharingStarted.Lazily, 1)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@@ -43,7 +43,6 @@ class StorageManager(
|
|||||||
}
|
}
|
||||||
_changes.send(Unit)
|
_changes.send(Unit)
|
||||||
}
|
}
|
||||||
.flowOn(Dispatchers.IO)
|
|
||||||
.launchIn(scope)
|
.launchIn(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user