Use Flow in ExtensionManager and SourceManager (#7547)
- Replace ExtensionManager relay and observable with Flow - Inverse SourceManager dependency - SourceManager observers ExtensionManager flow - Separate SourceData from SourceRepository as it created a circular dependency (cherry picked from commit 35ec5936587799f33a264f57729cb4b75c5a0f72) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt
This commit is contained in:
parent
dd68ef4ba8
commit
d8042f7182
@ -0,0 +1,23 @@
|
|||||||
|
package eu.kanade.data.source
|
||||||
|
|
||||||
|
import eu.kanade.data.DatabaseHandler
|
||||||
|
import eu.kanade.domain.source.model.SourceData
|
||||||
|
import eu.kanade.domain.source.repository.SourceDataRepository
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
class SourceDataRepositoryImpl(
|
||||||
|
private val handler: DatabaseHandler,
|
||||||
|
) : SourceDataRepository {
|
||||||
|
|
||||||
|
override fun subscribeAll(): Flow<List<SourceData>> {
|
||||||
|
return handler.subscribeToList { sourcesQueries.findAll(sourceDataMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun getSourceData(id: Long): SourceData? {
|
||||||
|
return handler.awaitOneOrNull { sourcesQueries.findOne(id, sourceDataMapper) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun upsertSourceData(id: Long, lang: String, name: String) {
|
||||||
|
handler.await { sourcesQueries.upsert(id, lang, name) }
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@ package eu.kanade.data.source
|
|||||||
|
|
||||||
import eu.kanade.data.DatabaseHandler
|
import eu.kanade.data.DatabaseHandler
|
||||||
import eu.kanade.domain.source.model.Source
|
import eu.kanade.domain.source.model.Source
|
||||||
import eu.kanade.domain.source.model.SourceData
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
import eu.kanade.domain.source.repository.SourceRepository
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
import eu.kanade.tachiyomi.source.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
import eu.kanade.tachiyomi.source.SourceManager
|
||||||
@ -51,12 +50,4 @@ class SourceRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getSourceData(id: Long): SourceData? {
|
|
||||||
return handler.awaitOneOrNull { sourcesQueries.getSourceData(id, sourceDataMapper) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun upsertSourceData(id: Long, lang: String, name: String) {
|
|
||||||
handler.await { sourcesQueries.upsert(id, lang, name) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import eu.kanade.data.category.CategoryRepositoryImpl
|
|||||||
import eu.kanade.data.chapter.ChapterRepositoryImpl
|
import eu.kanade.data.chapter.ChapterRepositoryImpl
|
||||||
import eu.kanade.data.history.HistoryRepositoryImpl
|
import eu.kanade.data.history.HistoryRepositoryImpl
|
||||||
import eu.kanade.data.manga.MangaRepositoryImpl
|
import eu.kanade.data.manga.MangaRepositoryImpl
|
||||||
|
import eu.kanade.data.source.SourceDataRepositoryImpl
|
||||||
import eu.kanade.data.source.SourceRepositoryImpl
|
import eu.kanade.data.source.SourceRepositoryImpl
|
||||||
import eu.kanade.data.track.TrackRepositoryImpl
|
import eu.kanade.data.track.TrackRepositoryImpl
|
||||||
import eu.kanade.domain.category.interactor.CreateCategoryWithName
|
import eu.kanade.domain.category.interactor.CreateCategoryWithName
|
||||||
@ -47,14 +48,13 @@ import eu.kanade.domain.manga.interactor.UpdateManga
|
|||||||
import eu.kanade.domain.manga.repository.MangaRepository
|
import eu.kanade.domain.manga.repository.MangaRepository
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
import eu.kanade.domain.source.interactor.GetLanguagesWithSources
|
||||||
import eu.kanade.domain.source.interactor.GetSourceData
|
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
import eu.kanade.domain.source.interactor.GetSourcesWithFavoriteCount
|
||||||
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
|
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||||
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
import eu.kanade.domain.source.interactor.SetMigrateSorting
|
||||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
import eu.kanade.domain.source.interactor.ToggleSource
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
import eu.kanade.domain.source.interactor.UpsertSourceData
|
import eu.kanade.domain.source.repository.SourceDataRepository
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
import eu.kanade.domain.source.repository.SourceRepository
|
||||||
import eu.kanade.domain.track.interactor.DeleteTrack
|
import eu.kanade.domain.track.interactor.DeleteTrack
|
||||||
import eu.kanade.domain.track.interactor.GetTracks
|
import eu.kanade.domain.track.interactor.GetTracks
|
||||||
@ -120,15 +120,14 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { GetExtensionLanguages(get(), get()) }
|
addFactory { GetExtensionLanguages(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
addSingletonFactory<SourceRepository> { SourceRepositoryImpl(get(), get()) }
|
||||||
|
addSingletonFactory<SourceDataRepository> { SourceDataRepositoryImpl(get()) }
|
||||||
addFactory { GetEnabledSources(get(), get()) }
|
addFactory { GetEnabledSources(get(), get()) }
|
||||||
addFactory { GetLanguagesWithSources(get(), get()) }
|
addFactory { GetLanguagesWithSources(get(), get()) }
|
||||||
addFactory { GetSourceData(get()) }
|
|
||||||
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
|
addFactory { GetSourcesWithFavoriteCount(get(), get()) }
|
||||||
addFactory { GetSourcesWithNonLibraryManga(get()) }
|
addFactory { GetSourcesWithNonLibraryManga(get()) }
|
||||||
addFactory { SetMigrateSorting(get()) }
|
addFactory { SetMigrateSorting(get()) }
|
||||||
addFactory { ToggleLanguage(get()) }
|
addFactory { ToggleLanguage(get()) }
|
||||||
addFactory { ToggleSource(get()) }
|
addFactory { ToggleSource(get()) }
|
||||||
addFactory { ToggleSourcePin(get()) }
|
addFactory { ToggleSourcePin(get()) }
|
||||||
addFactory { UpsertSourceData(get()) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ class GetExtensionLanguages(
|
|||||||
fun subscribe(): Flow<List<String>> {
|
fun subscribe(): Flow<List<String>> {
|
||||||
return combine(
|
return combine(
|
||||||
preferences.enabledLanguages().asFlow(),
|
preferences.enabledLanguages().asFlow(),
|
||||||
extensionManager.getAvailableExtensionsObservable().asFlow(),
|
extensionManager.getAvailableExtensionsFlow(),
|
||||||
) { enabledLanguage, availableExtensions ->
|
) { enabledLanguage, availableExtensions ->
|
||||||
availableExtensions
|
availableExtensions
|
||||||
.map { it.lang }
|
.map { it.lang }
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package eu.kanade.domain.extension.interactor
|
package eu.kanade.domain.extension.interactor
|
||||||
|
|
||||||
import eu.kanade.core.util.asFlow
|
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
@ -15,7 +14,7 @@ class GetExtensionUpdates(
|
|||||||
fun subscribe(): Flow<List<Extension.Installed>> {
|
fun subscribe(): Flow<List<Extension.Installed>> {
|
||||||
val showNsfwSources = preferences.showNsfwSource().get()
|
val showNsfwSources = preferences.showNsfwSource().get()
|
||||||
|
|
||||||
return extensionManager.getInstalledExtensionsObservable().asFlow()
|
return extensionManager.getInstalledExtensionsFlow()
|
||||||
.map { installed ->
|
.map { installed ->
|
||||||
installed
|
installed
|
||||||
.filter { it.hasUpdate && (showNsfwSources || it.isNsfw.not()) }
|
.filter { it.hasUpdate && (showNsfwSources || it.isNsfw.not()) }
|
||||||
|
@ -19,9 +19,9 @@ class GetExtensions(
|
|||||||
|
|
||||||
return combine(
|
return combine(
|
||||||
preferences.enabledLanguages().asFlow(),
|
preferences.enabledLanguages().asFlow(),
|
||||||
extensionManager.getInstalledExtensionsObservable().asFlow(),
|
extensionManager.getInstalledExtensionsFlow(),
|
||||||
extensionManager.getUntrustedExtensionsObservable().asFlow(),
|
extensionManager.getUntrustedExtensionsFlow(),
|
||||||
extensionManager.getAvailableExtensionsObservable().asFlow(),
|
extensionManager.getAvailableExtensionsFlow(),
|
||||||
) { _activeLanguages, _installed, _untrusted, _available ->
|
) { _activeLanguages, _installed, _untrusted, _available ->
|
||||||
|
|
||||||
val installed = _installed
|
val installed = _installed
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.model.SourceData
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
|
||||||
import logcat.LogPriority
|
|
||||||
|
|
||||||
class GetSourceData(
|
|
||||||
private val repository: SourceRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(id: Long): SourceData? {
|
|
||||||
return try {
|
|
||||||
repository.getSourceData(id)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.ERROR, e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
package eu.kanade.domain.source.interactor
|
|
||||||
|
|
||||||
import eu.kanade.domain.source.model.SourceData
|
|
||||||
import eu.kanade.domain.source.repository.SourceRepository
|
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
|
||||||
import logcat.LogPriority
|
|
||||||
|
|
||||||
class UpsertSourceData(
|
|
||||||
private val repository: SourceRepository,
|
|
||||||
) {
|
|
||||||
|
|
||||||
suspend fun await(sourceData: SourceData) {
|
|
||||||
try {
|
|
||||||
repository.upsertSourceData(sourceData.id, sourceData.lang, sourceData.name)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
logcat(LogPriority.ERROR, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.domain.source.repository
|
||||||
|
|
||||||
|
import eu.kanade.domain.source.model.SourceData
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
|
interface SourceDataRepository {
|
||||||
|
fun subscribeAll(): Flow<List<SourceData>>
|
||||||
|
|
||||||
|
suspend fun getSourceData(id: Long): SourceData?
|
||||||
|
|
||||||
|
suspend fun upsertSourceData(id: Long, lang: String, name: String)
|
||||||
|
}
|
@ -1,7 +1,6 @@
|
|||||||
package eu.kanade.domain.source.repository
|
package eu.kanade.domain.source.repository
|
||||||
|
|
||||||
import eu.kanade.domain.source.model.Source
|
import eu.kanade.domain.source.model.Source
|
||||||
import eu.kanade.domain.source.model.SourceData
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import eu.kanade.tachiyomi.source.Source as LoadedSource
|
import eu.kanade.tachiyomi.source.Source as LoadedSource
|
||||||
|
|
||||||
@ -14,8 +13,4 @@ interface SourceRepository {
|
|||||||
fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
|
fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
|
||||||
|
|
||||||
fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>>
|
fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>>
|
||||||
|
|
||||||
suspend fun getSourceData(id: Long): SourceData?
|
|
||||||
|
|
||||||
suspend fun upsertSourceData(id: Long, lang: String, name: String)
|
|
||||||
}
|
}
|
||||||
|
@ -101,10 +101,10 @@ class AppModule(val app: Application) : InjektModule {
|
|||||||
|
|
||||||
addSingletonFactory { NetworkHelper(app) }
|
addSingletonFactory { NetworkHelper(app) }
|
||||||
|
|
||||||
addSingletonFactory { SourceManager(app).also { get<ExtensionManager>().init(it) } }
|
|
||||||
|
|
||||||
addSingletonFactory { ExtensionManager(app) }
|
addSingletonFactory { ExtensionManager(app) }
|
||||||
|
|
||||||
|
addSingletonFactory { SourceManager(app, get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory { DownloadManager(app) }
|
addSingletonFactory { DownloadManager(app) }
|
||||||
|
|
||||||
addSingletonFactory { TrackManager(app) }
|
addSingletonFactory { TrackManager(app) }
|
||||||
|
@ -15,7 +15,6 @@ import eu.kanade.tachiyomi.extension.util.ExtensionInstallReceiver
|
|||||||
import eu.kanade.tachiyomi.extension.util.ExtensionInstaller
|
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.source.Source
|
import eu.kanade.tachiyomi.source.Source
|
||||||
import eu.kanade.tachiyomi.source.SourceManager
|
|
||||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||||
import eu.kanade.tachiyomi.util.preference.plusAssign
|
import eu.kanade.tachiyomi.util.preference.plusAssign
|
||||||
import eu.kanade.tachiyomi.util.system.logcat
|
import eu.kanade.tachiyomi.util.system.logcat
|
||||||
@ -102,22 +101,23 @@ class ExtensionManager(
|
|||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Relay used to notify the available extensions.
|
|
||||||
*/
|
|
||||||
private val availableExtensionsRelay = BehaviorRelay.create<List<Extension.Available>>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the currently available extensions.
|
* List of the currently available extensions.
|
||||||
*/
|
*/
|
||||||
var availableExtensions = emptyList<Extension.Available>()
|
var availableExtensions = emptyList<Extension.Available>()
|
||||||
private set(value) {
|
private set(value) {
|
||||||
field = value
|
field = value
|
||||||
availableExtensionsRelay.call(value)
|
availableExtensionsFlow.value = field
|
||||||
updatedInstalledExtensionsStatuses(value)
|
updatedInstalledExtensionsStatuses(value)
|
||||||
setupAvailableExtensionsSourcesDataMap(value)
|
setupAvailableExtensionsSourcesDataMap(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val availableExtensionsFlow = MutableStateFlow(availableExtensions)
|
||||||
|
|
||||||
|
fun getAvailableExtensionsFlow(): StateFlow<List<Extension.Available>> {
|
||||||
|
return availableExtensionsFlow.asStateFlow()
|
||||||
|
}
|
||||||
|
|
||||||
private var availableExtensionsSourcesData: Map<Long, SourceData> = mapOf()
|
private var availableExtensionsSourcesData: Map<Long, SourceData> = mapOf()
|
||||||
|
|
||||||
private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
|
private fun setupAvailableExtensionsSourcesDataMap(extensions: List<Extension.Available>) {
|
||||||
@ -133,30 +133,22 @@ class ExtensionManager(
|
|||||||
var unalteredAvailableExtensions = emptyList<Extension.Available>()
|
var unalteredAvailableExtensions = emptyList<Extension.Available>()
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
/**
|
|
||||||
* Relay used to notify the untrusted extensions.
|
|
||||||
*/
|
|
||||||
private val untrustedExtensionsRelay = BehaviorRelay.create<List<Extension.Untrusted>>()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of the currently untrusted extensions.
|
* List of the currently untrusted extensions.
|
||||||
*/
|
*/
|
||||||
var untrustedExtensions = emptyList<Extension.Untrusted>()
|
var untrustedExtensions = emptyList<Extension.Untrusted>()
|
||||||
private set(value) {
|
private set(value) {
|
||||||
field = value
|
field = value
|
||||||
untrustedExtensionsRelay.call(value)
|
untrustedExtensionsFlow.value = field
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private val untrustedExtensionsFlow = MutableStateFlow(untrustedExtensions)
|
||||||
* The source manager where the sources of the extensions are added.
|
|
||||||
*/
|
|
||||||
private lateinit var sourceManager: SourceManager
|
|
||||||
|
|
||||||
/**
|
fun getUntrustedExtensionsFlow(): StateFlow<List<Extension.Untrusted>> {
|
||||||
* Initializes this manager with the given source manager.
|
return untrustedExtensionsFlow.asStateFlow()
|
||||||
*/
|
}
|
||||||
fun init(sourceManager: SourceManager) {
|
|
||||||
this.sourceManager = sourceManager
|
init {
|
||||||
initExtensions()
|
initExtensions()
|
||||||
ExtensionInstallReceiver(InstallationListener()).register(context)
|
ExtensionInstallReceiver(InstallationListener()).register(context)
|
||||||
}
|
}
|
||||||
@ -170,9 +162,6 @@ class ExtensionManager(
|
|||||||
installedExtensions = extensions
|
installedExtensions = extensions
|
||||||
.filterIsInstance<LoadResult.Success>()
|
.filterIsInstance<LoadResult.Success>()
|
||||||
.map { it.extension }
|
.map { it.extension }
|
||||||
installedExtensions
|
|
||||||
.flatMap { it.sources }
|
|
||||||
.forEach { sourceManager.registerSource(it) }
|
|
||||||
|
|
||||||
untrustedExtensions = extensions
|
untrustedExtensions = extensions
|
||||||
.filterIsInstance<LoadResult.Untrusted>()
|
.filterIsInstance<LoadResult.Untrusted>()
|
||||||
@ -198,27 +187,6 @@ class ExtensionManager(
|
|||||||
}
|
}
|
||||||
// EXH <--
|
// EXH <--
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the relay of the installed extensions as an observable.
|
|
||||||
*/
|
|
||||||
fun getInstalledExtensionsObservable(): Observable<List<Extension.Installed>> {
|
|
||||||
return installedExtensionsRelay.asObservable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the relay of the available extensions as an observable.
|
|
||||||
*/
|
|
||||||
fun getAvailableExtensionsObservable(): Observable<List<Extension.Available>> {
|
|
||||||
return availableExtensionsRelay.asObservable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the relay of the untrusted extensions as an observable.
|
|
||||||
*/
|
|
||||||
fun getUntrustedExtensionsObservable(): Observable<List<Extension.Untrusted>> {
|
|
||||||
return untrustedExtensionsRelay.asObservable()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the available extensions in the [api] and updates [availableExtensions].
|
* Finds the available extensions in the [api] and updates [availableExtensions].
|
||||||
*/
|
*/
|
||||||
@ -378,7 +346,6 @@ class ExtensionManager(
|
|||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
installedExtensions += extension
|
installedExtensions += extension
|
||||||
extension.sources.forEach { sourceManager.registerSource(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -399,11 +366,9 @@ class ExtensionManager(
|
|||||||
val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }
|
val oldExtension = mutInstalledExtensions.find { it.pkgName == extension.pkgName }
|
||||||
if (oldExtension != null) {
|
if (oldExtension != null) {
|
||||||
mutInstalledExtensions -= oldExtension
|
mutInstalledExtensions -= oldExtension
|
||||||
extension.sources.forEach { sourceManager.unregisterSource(it) }
|
|
||||||
}
|
}
|
||||||
mutInstalledExtensions += extension
|
mutInstalledExtensions += extension
|
||||||
installedExtensions = mutInstalledExtensions
|
installedExtensions = mutInstalledExtensions
|
||||||
extension.sources.forEach { sourceManager.registerSource(it) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -416,7 +381,6 @@ class ExtensionManager(
|
|||||||
val installedExtension = installedExtensions.find { it.pkgName == pkgName }
|
val installedExtension = installedExtensions.find { it.pkgName == pkgName }
|
||||||
if (installedExtension != null) {
|
if (installedExtension != null) {
|
||||||
installedExtensions -= installedExtension
|
installedExtensions -= installedExtension
|
||||||
installedExtension.sources.forEach { sourceManager.unregisterSource(it) }
|
|
||||||
}
|
}
|
||||||
val untrustedExtension = untrustedExtensions.find { it.pkgName == pkgName }
|
val untrustedExtension = untrustedExtensions.find { it.pkgName == pkgName }
|
||||||
if (untrustedExtension != null) {
|
if (untrustedExtension != null) {
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package eu.kanade.tachiyomi.source
|
package eu.kanade.tachiyomi.source
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.kanade.domain.source.interactor.GetSourceData
|
|
||||||
import eu.kanade.domain.source.interactor.UpsertSourceData
|
|
||||||
import eu.kanade.domain.source.model.SourceData
|
import eu.kanade.domain.source.model.SourceData
|
||||||
|
import eu.kanade.domain.source.repository.SourceDataRepository
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
@ -21,7 +20,6 @@ import eu.kanade.tachiyomi.source.online.english.EightMuses
|
|||||||
import eu.kanade.tachiyomi.source.online.english.HBrowse
|
import eu.kanade.tachiyomi.source.online.english.HBrowse
|
||||||
import eu.kanade.tachiyomi.source.online.english.Pururin
|
import eu.kanade.tachiyomi.source.online.english.Pururin
|
||||||
import eu.kanade.tachiyomi.source.online.english.Tsumino
|
import eu.kanade.tachiyomi.source.online.english.Tsumino
|
||||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
|
||||||
import exh.log.xLogD
|
import exh.log.xLogD
|
||||||
import exh.source.BlacklistedSources
|
import exh.source.BlacklistedSources
|
||||||
import exh.source.DelegatedHttpSource
|
import exh.source.DelegatedHttpSource
|
||||||
@ -30,6 +28,7 @@ import exh.source.EIGHTMUSES_SOURCE_ID
|
|||||||
import exh.source.EXH_SOURCE_ID
|
import exh.source.EXH_SOURCE_ID
|
||||||
import exh.source.EnhancedHttpSource
|
import exh.source.EnhancedHttpSource
|
||||||
import exh.source.HBROWSE_SOURCE_ID
|
import exh.source.HBROWSE_SOURCE_ID
|
||||||
|
import exh.source.MERGED_SOURCE_ID
|
||||||
import exh.source.PERV_EDEN_EN_SOURCE_ID
|
import exh.source.PERV_EDEN_EN_SOURCE_ID
|
||||||
import exh.source.PERV_EDEN_IT_SOURCE_ID
|
import exh.source.PERV_EDEN_IT_SOURCE_ID
|
||||||
import exh.source.PURURIN_SOURCE_ID
|
import exh.source.PURURIN_SOURCE_ID
|
||||||
@ -40,11 +39,10 @@ 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.drop
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.flow.update
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import tachiyomi.source.model.ChapterInfo
|
import tachiyomi.source.model.ChapterInfo
|
||||||
@ -52,46 +50,102 @@ import tachiyomi.source.model.MangaInfo
|
|||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
class SourceManager(private val context: Context) {
|
class SourceManager(
|
||||||
|
private val context: Context,
|
||||||
|
private val extensionManager: ExtensionManager,
|
||||||
|
private val sourceRepository: SourceDataRepository,
|
||||||
|
) {
|
||||||
|
|
||||||
private val extensionManager: ExtensionManager by injectLazy()
|
private val scope = CoroutineScope(Job() + Dispatchers.IO)
|
||||||
private val getSourceData: GetSourceData by injectLazy()
|
|
||||||
private val upsertSourceData: UpsertSourceData by injectLazy()
|
private var sourcesMap = emptyMap<Long, Source>()
|
||||||
|
set(value) {
|
||||||
|
field = value
|
||||||
|
sourcesMapFlow.value = field
|
||||||
|
}
|
||||||
|
|
||||||
|
private val sourcesMapFlow = MutableStateFlow(sourcesMap)
|
||||||
|
|
||||||
private val sourcesMap = mutableMapOf<Long, Source>()
|
|
||||||
private val stubSourcesMap = mutableMapOf<Long, StubSource>()
|
private val stubSourcesMap = mutableMapOf<Long, StubSource>()
|
||||||
|
|
||||||
private val _catalogueSources: MutableStateFlow<List<CatalogueSource>> = MutableStateFlow(listOf())
|
val catalogueSources: Flow<List<CatalogueSource>> = sourcesMapFlow.map { it.values.filterIsInstance<CatalogueSource>() }
|
||||||
val catalogueSources: Flow<List<CatalogueSource>> = _catalogueSources
|
val onlineSources: Flow<List<HttpSource>> = catalogueSources.map { sources -> sources.filterIsInstance<HttpSource>() }
|
||||||
val onlineSources: Flow<List<HttpSource>> =
|
|
||||||
_catalogueSources.map { sources -> sources.filterIsInstance<HttpSource>() }
|
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
private val prefs: PreferencesHelper by injectLazy()
|
private val preferences: PreferencesHelper by injectLazy()
|
||||||
|
|
||||||
private val scope = CoroutineScope(Job() + Dispatchers.Main)
|
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
init {
|
init {
|
||||||
createInternalSources().forEach { registerSource(it) }
|
scope.launch {
|
||||||
|
extensionManager.getInstalledExtensionsFlow()
|
||||||
// SY -->
|
// SY -->
|
||||||
// Create internal sources
|
.combine(preferences.enableExhentai().asFlow()) { extensions, enableExhentai ->
|
||||||
createEHSources().forEach { registerSource(it) }
|
extensions to enableExhentai
|
||||||
|
|
||||||
// Watch the preference and manage Exhentai
|
|
||||||
prefs.enableExhentai().asFlow()
|
|
||||||
.drop(1)
|
|
||||||
.onEach {
|
|
||||||
if (it) {
|
|
||||||
registerSource(EHentai(EXH_SOURCE_ID, true, context))
|
|
||||||
} else {
|
|
||||||
sourcesMap.remove(EXH_SOURCE_ID)
|
|
||||||
}
|
}
|
||||||
}.launchIn(scope)
|
// SY <--
|
||||||
|
.collectLatest { (extensions, enableExhentai) ->
|
||||||
|
val mutableMap = mutableMapOf<Long, Source>(LocalSource.ID to LocalSource(context)).apply {
|
||||||
|
// SY -->
|
||||||
|
put(EH_SOURCE_ID, EHentai(EH_SOURCE_ID, false, context))
|
||||||
|
if (enableExhentai) {
|
||||||
|
put(EXH_SOURCE_ID, EHentai(EXH_SOURCE_ID, true, context))
|
||||||
|
}
|
||||||
|
put(MERGED_SOURCE_ID, MergedSource())
|
||||||
|
// SY <--
|
||||||
|
}
|
||||||
|
extensions.forEach { extension ->
|
||||||
|
extension.sources.mapNotNull { it.toInternalSource() }.forEach {
|
||||||
|
mutableMap[it.id] = it
|
||||||
|
registerStubSource(it.toSourceData())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sourcesMap = mutableMap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
registerSource(MergedSource())
|
scope.launch {
|
||||||
// SY <--
|
sourceRepository.subscribeAll()
|
||||||
|
.collectLatest { sources ->
|
||||||
|
val mutableMap = stubSourcesMap.toMutableMap()
|
||||||
|
sources.forEach {
|
||||||
|
mutableMap[it.id] = StubSource(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Source.toInternalSource(): Source? {
|
||||||
|
// EXH -->
|
||||||
|
val sourceQName = this::class.qualifiedName
|
||||||
|
val factories = DELEGATED_SOURCES.entries.filter { it.value.factory }.map { it.value.originalSourceQualifiedClassName }
|
||||||
|
val delegate = if (sourceQName != null) {
|
||||||
|
val matched = factories.find { sourceQName.startsWith(it) }
|
||||||
|
if (matched != null) {
|
||||||
|
DELEGATED_SOURCES[matched]
|
||||||
|
} else DELEGATED_SOURCES[sourceQName]
|
||||||
|
} else null
|
||||||
|
val newSource = if (this is HttpSource && delegate != null) {
|
||||||
|
xLogD("Delegating source: %s -> %s!", sourceQName, delegate.newSourceClass.qualifiedName)
|
||||||
|
val enhancedSource = EnhancedHttpSource(
|
||||||
|
this,
|
||||||
|
delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(this, context),
|
||||||
|
)
|
||||||
|
|
||||||
|
currentDelegatedSources[enhancedSource.originalSource.id] = DelegatedSource(
|
||||||
|
enhancedSource.originalSource.name,
|
||||||
|
enhancedSource.originalSource.id,
|
||||||
|
enhancedSource.originalSource::class.qualifiedName ?: delegate.originalSourceQualifiedClassName,
|
||||||
|
(enhancedSource.enhancedSource as DelegatedHttpSource)::class,
|
||||||
|
delegate.factory,
|
||||||
|
)
|
||||||
|
enhancedSource
|
||||||
|
} else this
|
||||||
|
|
||||||
|
return if (id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) {
|
||||||
|
xLogD("Removing blacklisted source: (id: %s, name: %s, lang: %s)!", id, name, (this as? CatalogueSource)?.lang)
|
||||||
|
null
|
||||||
|
} else newSource
|
||||||
|
// EXH <--
|
||||||
}
|
}
|
||||||
|
|
||||||
fun get(sourceKey: Long): Source? {
|
fun get(sourceKey: Long): Source? {
|
||||||
@ -127,91 +181,15 @@ class SourceManager(private val context: Context) {
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
internal fun registerSource(source: Source) {
|
|
||||||
// EXH -->
|
|
||||||
val sourceQName = source::class.qualifiedName
|
|
||||||
val factories = DELEGATED_SOURCES.entries.filter { it.value.factory }.map { it.value.originalSourceQualifiedClassName }
|
|
||||||
val delegate = if (sourceQName != null) {
|
|
||||||
val matched = factories.find { sourceQName.startsWith(it) }
|
|
||||||
if (matched != null) {
|
|
||||||
DELEGATED_SOURCES[matched]
|
|
||||||
} else DELEGATED_SOURCES[sourceQName]
|
|
||||||
} else null
|
|
||||||
val newSource = if (source is HttpSource && delegate != null) {
|
|
||||||
xLogD("Delegating source: %s -> %s!", sourceQName, delegate.newSourceClass.qualifiedName)
|
|
||||||
val enhancedSource = EnhancedHttpSource(
|
|
||||||
source,
|
|
||||||
delegate.newSourceClass.constructors.find { it.parameters.size == 2 }!!.call(source, context),
|
|
||||||
)
|
|
||||||
|
|
||||||
currentDelegatedSources[enhancedSource.originalSource.id] = DelegatedSource(
|
|
||||||
enhancedSource.originalSource.name,
|
|
||||||
enhancedSource.originalSource.id,
|
|
||||||
enhancedSource.originalSource::class.qualifiedName ?: delegate.originalSourceQualifiedClassName,
|
|
||||||
(enhancedSource.enhancedSource as DelegatedHttpSource)::class,
|
|
||||||
delegate.factory,
|
|
||||||
)
|
|
||||||
enhancedSource
|
|
||||||
} else source
|
|
||||||
|
|
||||||
if (source.id in BlacklistedSources.BLACKLISTED_EXT_SOURCES) {
|
|
||||||
xLogD("Removing blacklisted source: (id: %s, name: %s, lang: %s)!", source.id, source.name, (source as? CatalogueSource)?.lang)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// EXH <--
|
|
||||||
|
|
||||||
if (!sourcesMap.containsKey(source.id)) {
|
|
||||||
sourcesMap[source.id] = newSource
|
|
||||||
}
|
|
||||||
registerStubSource(source.toSourceData())
|
|
||||||
triggerCatalogueSources()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun registerStubSource(sourceData: SourceData) {
|
private fun registerStubSource(sourceData: SourceData) {
|
||||||
launchIO {
|
scope.launch {
|
||||||
val dbSourceData = getSourceData.await(sourceData.id)
|
val (id, lang, name) = sourceData
|
||||||
|
sourceRepository.upsertSourceData(id, lang, name)
|
||||||
if (dbSourceData != sourceData) {
|
|
||||||
upsertSourceData.await(sourceData)
|
|
||||||
}
|
|
||||||
if (stubSourcesMap[sourceData.id]?.toSourceData() != sourceData) {
|
|
||||||
stubSourcesMap[sourceData.id] = StubSource(sourceData)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun unregisterSource(source: Source) {
|
|
||||||
sourcesMap.remove(source.id)
|
|
||||||
triggerCatalogueSources()
|
|
||||||
// SY -->
|
|
||||||
currentDelegatedSources.remove(source.id)
|
|
||||||
// SY <--
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun triggerCatalogueSources() {
|
|
||||||
_catalogueSources.update {
|
|
||||||
sourcesMap.values.filterIsInstance<CatalogueSource>()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createInternalSources(): List<Source> = listOf(
|
|
||||||
LocalSource(context),
|
|
||||||
)
|
|
||||||
|
|
||||||
// SY -->
|
|
||||||
private fun createEHSources(): List<Source> {
|
|
||||||
val sources = listOf<HttpSource>(
|
|
||||||
EHentai(EH_SOURCE_ID, false, context),
|
|
||||||
)
|
|
||||||
return if (prefs.enableExhentai().get()) {
|
|
||||||
sources + EHentai(EXH_SOURCE_ID, true, context)
|
|
||||||
} else sources
|
|
||||||
}
|
|
||||||
|
|
||||||
// SY <--
|
|
||||||
|
|
||||||
private suspend fun createStubSource(id: Long): StubSource {
|
private suspend fun createStubSource(id: Long): StubSource {
|
||||||
getSourceData.await(id)?.let {
|
sourceRepository.getSourceData(id)?.let {
|
||||||
return StubSource(it)
|
return StubSource(it)
|
||||||
}
|
}
|
||||||
extensionManager.getSourceData(id)?.let {
|
extensionManager.getSourceData(id)?.let {
|
||||||
|
@ -4,7 +4,11 @@ CREATE TABLE sources(
|
|||||||
name TEXT NOT NULL
|
name TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
getSourceData:
|
findAll:
|
||||||
|
SELECT *
|
||||||
|
FROM sources;
|
||||||
|
|
||||||
|
findOne:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM sources
|
FROM sources
|
||||||
WHERE _id = :id;
|
WHERE _id = :id;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user