Refactor: Provide manga parsers via DI

This commit is contained in:
Koitharu
2020-10-20 21:45:15 +03:00
parent 6f3ae19345
commit a5fba83510
24 changed files with 95 additions and 104 deletions

View File

@@ -103,4 +103,5 @@ dependencies {
testImplementation 'junit:junit:4.13.1' testImplementation 'junit:junit:4.13.1'
testImplementation 'org.json:json:20200518' testImplementation 'org.json:json:20200518'
testImplementation 'org.koin:koin-test:2.2.0-rc-2'
} }

View File

@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.github.githubModule
import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.core.local.PagesCache
import org.koitharu.kotatsu.core.network.networkModule import org.koitharu.kotatsu.core.network.networkModule
import org.koitharu.kotatsu.core.parser.LocalMangaRepository import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.parser.parserModule
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaDataRepository import org.koitharu.kotatsu.domain.MangaDataRepository
import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.MangaLoaderContext
@@ -59,11 +60,12 @@ class KotatsuApp : Application() {
networkModule, networkModule,
databaseModule, databaseModule,
githubModule, githubModule,
parserModule,
uiModule, uiModule,
module { module {
single { FavouritesRepository(get()) } single { FavouritesRepository(get()) }
single { HistoryRepository(get()) } single { HistoryRepository(get()) }
single { TrackingRepository(get()) } single { TrackingRepository(get(), get()) }
single { MangaDataRepository(get()) } single { MangaDataRepository(get()) }
single { MangaSearchRepository() } single { MangaSearchRepository() }
single { MangaLoaderContext() } single { MangaLoaderContext() }

View File

@@ -2,6 +2,8 @@ package org.koitharu.kotatsu.core.model
import android.os.Parcelable import android.os.Parcelable
import kotlinx.android.parcel.Parcelize import kotlinx.android.parcel.Parcelize
import org.koin.core.context.GlobalContext
import org.koin.core.error.NoBeanDefFoundException
import org.koitharu.kotatsu.core.parser.LocalMangaRepository import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.parser.MangaRepository import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.site.* import org.koitharu.kotatsu.core.parser.site.*
@@ -24,6 +26,10 @@ enum class MangaSource(
MANGATOWN("MangaTown", "en", MangaTownRepository::class.java), MANGATOWN("MangaTown", "en", MangaTownRepository::class.java),
MANGALIB("MangaLib", "ru", MangaLibRepository::class.java), MANGALIB("MangaLib", "ru", MangaLibRepository::class.java),
NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java), NUDEMOON("Nude-Moon", "ru", NudeMoonRepository::class.java),
MANGAREAD("MangaRead", "en", MangareadRepository::class.java), MANGAREAD("MangaRead", "en", MangareadRepository::class.java);
// HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java) // HENTAILIB("HentaiLib", "ru", HentaiLibRepository::class.java)
@get:Throws(NoBeanDefFoundException::class)
val repository: MangaRepository
get() = GlobalContext.get().get(cls.kotlin)
} }

View File

@@ -7,8 +7,6 @@ import android.webkit.MimeTypeMap
import androidx.collection.ArraySet import androidx.collection.ArraySet
import androidx.core.net.toFile import androidx.core.net.toFile
import androidx.core.net.toUri import androidx.core.net.toUri
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.local.CbzFilter import org.koitharu.kotatsu.core.local.CbzFilter
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.domain.local.MangaIndex import org.koitharu.kotatsu.domain.local.MangaIndex
@@ -23,9 +21,7 @@ import java.util.*
import java.util.zip.ZipEntry import java.util.zip.ZipEntry
import java.util.zip.ZipFile import java.util.zip.ZipFile
class LocalMangaRepository : MangaRepository, KoinComponent { class LocalMangaRepository(private val context: Context) : MangaRepository {
private val context by inject<Context>()
override suspend fun getList( override suspend fun getList(
offset: Int, offset: Int,

View File

@@ -2,8 +2,21 @@ package org.koitharu.kotatsu.core.parser
import org.koin.dsl.bind import org.koin.dsl.bind
import org.koin.dsl.module import org.koin.dsl.module
import org.koitharu.kotatsu.core.parser.site.*
val parserModule val parserModule
get() = module { get() = module {
single { LocalMangaRepository() } bind MangaRepository::class single { LocalMangaRepository(get()) } bind MangaRepository::class
factory { ReadmangaRepository(get()) } bind MangaRepository::class
factory { MintMangaRepository(get()) } bind MangaRepository::class
factory { SelfMangaRepository(get()) } bind MangaRepository::class
factory { MangaChanRepository(get()) } bind MangaRepository::class
factory { DesuMeRepository(get()) } bind MangaRepository::class
factory { HenChanRepository(get()) } bind MangaRepository::class
factory { YaoiChanRepository(get()) } bind MangaRepository::class
factory { MangaTownRepository(get()) } bind MangaRepository::class
factory { MangaLibRepository(get()) } bind MangaRepository::class
factory { NudeMoonRepository(get()) } bind MangaRepository::class
factory { MangareadRepository(get()) } bind MangaRepository::class
} }

View File

@@ -2,20 +2,11 @@ package org.koitharu.kotatsu.domain
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.get import org.koin.core.component.get
import org.koin.core.component.inject
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import java.lang.ref.WeakReference
import java.util.*
object MangaProviderFactory : KoinComponent { object MangaProviderFactory : KoinComponent {
private val loaderContext by inject<MangaLoaderContext>()
private val cache =
EnumMap<MangaSource, WeakReference<MangaRepository>>(MangaSource::class.java)
fun getSources(includeHidden: Boolean): List<MangaSource> { fun getSources(includeHidden: Boolean): List<MangaSource> {
val settings = get<AppSettings>() val settings = get<AppSettings>()
val list = MangaSource.values().toList() - MangaSource.LOCAL val list = MangaSource.values().toList() - MangaSource.LOCAL
@@ -33,39 +24,4 @@ object MangaProviderFactory : KoinComponent {
} }
} }
} }
@Deprecated("Use DI")
fun createLocal(): LocalMangaRepository {
var instance = cache[MangaSource.LOCAL]?.get()
if (instance == null) {
synchronized(cache) {
instance = cache[MangaSource.LOCAL]?.get()
if (instance == null) {
instance = LocalMangaRepository()
cache[MangaSource.LOCAL] = WeakReference<MangaRepository>(instance)
}
}
}
return instance as LocalMangaRepository
}
@Throws(Throwable::class)
fun create(source: MangaSource): MangaRepository {
var instance = cache[source]?.get()
if (instance == null) {
synchronized(cache) {
instance = cache[source]?.get()
if (instance == null) {
instance = try {
source.cls.getDeclaredConstructor(MangaLoaderContext::class.java)
.newInstance(loaderContext)
} catch (e: NoSuchMethodException) {
source.cls.newInstance()
}
cache[source] = WeakReference(instance!!)
}
}
}
return instance!!
}
} }

View File

@@ -18,7 +18,7 @@ class MangaSearchRepository {
for (source in sources) { for (source in sources) {
val list = lists.getOrPut(source) { val list = lists.getOrPut(source) {
try { try {
MangaProviderFactory.create(source).getList(0, query, SortOrder.POPULARITY) source.repository.getList(0, query, SortOrder.POPULARITY)
} catch (e: Throwable) { } catch (e: Throwable) {
e.printStackTrace() e.printStackTrace()
emptyList<Manga>() emptyList<Manga>()

View File

@@ -27,7 +27,7 @@ object MangaUtils : KoinComponent {
suspend fun determineReaderMode(pages: List<MangaPage>): ReaderMode? { suspend fun determineReaderMode(pages: List<MangaPage>): ReaderMode? {
try { try {
val page = pages.medianOrNull() ?: return null val page = pages.medianOrNull() ?: return null
val url = MangaProviderFactory.create(page.source).getPageFullUrl(page) val url = page.source.repository.getPageFullUrl(page)
val uri = Uri.parse(url) val uri = Uri.parse(url)
val size = if (uri.scheme == "cbz") { val size = if (uri.scheme == "cbz") {
val zip = ZipFile(uri.schemeSpecificPart) val zip = ZipFile(uri.schemeSpecificPart)

View File

@@ -5,10 +5,13 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
import org.koitharu.kotatsu.core.db.entity.TrackEntity import org.koitharu.kotatsu.core.db.entity.TrackEntity
import org.koitharu.kotatsu.core.db.entity.TrackLogEntity import org.koitharu.kotatsu.core.db.entity.TrackLogEntity
import org.koitharu.kotatsu.core.model.* import org.koitharu.kotatsu.core.model.*
import org.koitharu.kotatsu.domain.MangaProviderFactory import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import java.util.* import java.util.*
class TrackingRepository(private val db: MangaDatabase) { class TrackingRepository(
private val db: MangaDatabase,
private val localMangaRepository: LocalMangaRepository
) {
suspend fun getNewChaptersCount(mangaId: Long): Int { suspend fun getNewChaptersCount(mangaId: Long): Int {
val entity = db.tracksDao.find(mangaId) ?: return 0 val entity = db.tracksDao.find(mangaId) ?: return 0
@@ -28,7 +31,7 @@ class TrackingRepository(private val db: MangaDatabase) {
.distinctBy { it.id } .distinctBy { it.id }
.mapNotNull { me -> .mapNotNull { me ->
val manga = if (me.source == MangaSource.LOCAL) { val manga = if (me.source == MangaSource.LOCAL) {
MangaProviderFactory.createLocal().getRemoteManga(me) localMangaRepository.getRemoteManga(me) // FIXME duplicating
} else { } else {
me me
} ?: return@mapNotNull null } ?: return@mapNotNull null

View File

@@ -17,6 +17,7 @@ abstract class BaseFragment(
fun stringArg(name: String) = StringArgumentDelegate(name) fun stringArg(name: String) = StringArgumentDelegate(name)
@Deprecated("Use extension", replaceWith = ReplaceWith("parcelableArgument(name)"))
fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name) fun <T : Parcelable> arg(name: String) = ParcelableArgumentDelegate<T>(name)
open fun getTitle(): CharSequence? = null open fun getTitle(): CharSequence? = null

View File

@@ -7,14 +7,13 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import moxy.InjectViewState import moxy.InjectViewState
import moxy.presenterScope import moxy.presenterScope
import org.koin.core.component.get
import org.koin.core.component.inject import org.koin.core.component.inject
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.exceptions.MangaNotFoundException import org.koitharu.kotatsu.core.exceptions.MangaNotFoundException
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.LocalMangaRepository import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.domain.MangaDataRepository import org.koitharu.kotatsu.domain.MangaDataRepository
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.MangaSearchRepository import org.koitharu.kotatsu.domain.MangaSearchRepository
import org.koitharu.kotatsu.domain.favourites.FavouritesRepository import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener
@@ -75,8 +74,8 @@ class MangaDetailsPresenter private constructor(private val key: Int) :
presenterScope.launch { presenterScope.launch {
try { try {
viewState.onLoadingStateChanged(true) viewState.onLoadingStateChanged(true)
val data = withContext(Dispatchers.IO) { val data = withContext(Dispatchers.Default) {
MangaProviderFactory.create(manga.source).getDetails(manga) manga.source.repository.getDetails(manga)
} }
viewState.onMangaUpdated(data) viewState.onMangaUpdated(data)
this@MangaDetailsPresenter.manga = data this@MangaDetailsPresenter.manga = data
@@ -98,8 +97,7 @@ class MangaDetailsPresenter private constructor(private val key: Int) :
viewState.onLoadingStateChanged(true) viewState.onLoadingStateChanged(true)
try { try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val repository = val repository = get<LocalMangaRepository>()
MangaProviderFactory.create(MangaSource.LOCAL) as LocalMangaRepository
val original = repository.getRemoteManga(manga) val original = repository.getRemoteManga(manga)
repository.delete(manga) || throw IOException("Unable to delete file") repository.delete(manga) || throw IOException("Unable to delete file")
safe { safe {

View File

@@ -14,13 +14,14 @@ import kotlinx.coroutines.sync.Mutex
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okio.IOException import okio.IOException
import org.koin.android.ext.android.get
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.core.local.PagesCache
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.parser.LocalMangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.local.MangaZip import org.koitharu.kotatsu.domain.local.MangaZip
import org.koitharu.kotatsu.ui.base.BaseService import org.koitharu.kotatsu.ui.base.BaseService
import org.koitharu.kotatsu.ui.base.dialog.CheckBoxAlertDialog import org.koitharu.kotatsu.ui.base.dialog.CheckBoxAlertDialog
@@ -87,7 +88,7 @@ class DownloadService : BaseService() {
checkNotNull(destination) { getString(R.string.cannot_find_available_storage) } checkNotNull(destination) { getString(R.string.cannot_find_available_storage) }
var output: MangaZip? = null var output: MangaZip? = null
try { try {
val repo = MangaProviderFactory.create(manga.source) val repo = manga.source.repository
val cover = safe { val cover = safe {
imageLoader.execute( imageLoader.execute(
ImageRequest.Builder(this@DownloadService) ImageRequest.Builder(this@DownloadService)
@@ -146,7 +147,7 @@ class DownloadService : BaseService() {
if (!output.compress()) { if (!output.compress()) {
throw RuntimeException("Cannot create target file") throw RuntimeException("Cannot create target file")
} }
val result = MangaProviderFactory.createLocal().getFromFile(output.file) val result = get<LocalMangaRepository>().getFromFile(output.file)
notification.setDone(result) notification.setDone(result)
notification.dismiss() notification.dismiss()
notification.update(manga.id.toInt().absoluteValue) notification.update(manga.id.toInt().absoluteValue)

View File

@@ -3,7 +3,6 @@ package org.koitharu.kotatsu.ui.list
import moxy.InjectViewState import moxy.InjectViewState
import org.koin.core.component.inject import org.koin.core.component.inject
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.history.HistoryRepository import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.ui.base.BasePresenter import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.reader.ReaderState import org.koitharu.kotatsu.ui.reader.ReaderState
@@ -19,7 +18,7 @@ class MainPresenter : BasePresenter<MainView>() {
?: throw EmptyHistoryException() ?: throw EmptyHistoryException()
val history = historyRepository.getOne(manga) ?: throw EmptyHistoryException() val history = historyRepository.getOne(manga) ?: throw EmptyHistoryException()
val state = ReaderState( val state = ReaderState(
MangaProviderFactory.create(manga.source).getDetails(manga), manga.source.repository.getDetails(manga),
history.chapterId, history.page, history.scroll history.chapterId, history.page, history.scroll
) )
viewState.onOpenReader(state) viewState.onOpenReader(state)

View File

@@ -9,16 +9,19 @@ import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.ui.list.MangaListFragment import org.koitharu.kotatsu.ui.list.MangaListFragment
import org.koitharu.kotatsu.ui.search.SearchActivity import org.koitharu.kotatsu.ui.search.SearchActivity
import org.koitharu.kotatsu.utils.ext.parcelableArgument
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
class RemoteListFragment : MangaListFragment<Unit>() { class RemoteListFragment : MangaListFragment<Unit>() {
private val presenter by moxyPresenter(factory = ::RemoteListPresenter) private val presenter by moxyPresenter {
RemoteListPresenter(source)
}
private val source by arg<MangaSource>(ARG_SOURCE) private val source by parcelableArgument<MangaSource>(ARG_SOURCE)
override fun onRequestMoreItems(offset: Int) { override fun onRequestMoreItems(offset: Int) {
presenter.loadList(source, offset) presenter.loadList(offset)
} }
override fun getTitle(): CharSequence? { override fun getTitle(): CharSequence? {
@@ -26,7 +29,7 @@ class RemoteListFragment : MangaListFragment<Unit>() {
} }
override fun onFilterChanged(filter: MangaFilter) { override fun onFilterChanged(filter: MangaFilter) {
presenter.applyFilter(source, filter) presenter.applyFilter(filter)
super.onFilterChanged(filter) super.onFilterChanged(filter)
} }

View File

@@ -9,22 +9,29 @@ import moxy.presenterScope
import org.koitharu.kotatsu.BuildConfig import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.core.model.MangaFilter import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.base.BasePresenter import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.list.MangaListView import org.koitharu.kotatsu.ui.list.MangaListView
@InjectViewState @InjectViewState
class RemoteListPresenter : BasePresenter<MangaListView<Unit>>() { class RemoteListPresenter(source: MangaSource) : BasePresenter<MangaListView<Unit>>() {
private val repository by lazy(LazyThreadSafetyMode.PUBLICATION) {
source.repository
}
private var isFilterInitialized = false private var isFilterInitialized = false
private var filter: MangaFilter? = null private var filter: MangaFilter? = null
fun loadList(source: MangaSource, offset: Int) { override fun onFirstViewAttach() {
super.onFirstViewAttach()
loadFilter()
}
fun loadList(offset: Int) {
presenterScope.launch { presenterScope.launch {
viewState.onLoadingStateChanged(true) viewState.onLoadingStateChanged(true)
try { try {
val list = withContext(Dispatchers.Default) { val list = withContext(Dispatchers.Default) {
MangaProviderFactory.create(source).getList( repository.getList(
offset = offset, offset = offset,
sortOrder = filter?.sortOrder, sortOrder = filter?.sortOrder,
tag = filter?.tag tag = filter?.tag
@@ -50,23 +57,23 @@ class RemoteListPresenter : BasePresenter<MangaListView<Unit>>() {
} }
} }
if (!isFilterInitialized) { if (!isFilterInitialized) {
loadFilter(source) loadFilter()
} }
} }
fun applyFilter(source: MangaSource, filter: MangaFilter) { fun applyFilter(filter: MangaFilter) {
this.filter = filter this.filter = filter
viewState.onListChanged(emptyList()) viewState.onListChanged(emptyList())
loadList(source, 0) loadList(0)
} }
private fun loadFilter(source: MangaSource) { private fun loadFilter() {
isFilterInitialized = true isFilterInitialized = true
presenterScope.launch { launchJob {
try { try {
val (sorts, tags) = withContext(Dispatchers.Default) { val (sorts, tags) = withContext(Dispatchers.Default) {
val repo = MangaProviderFactory.create(source) repository.sortOrders.sortedBy { it.ordinal } to repository.getTags()
repo.sortOrders.sortedBy { it.ordinal } to repo.getTags().sortedBy { it.title } .sortedBy { it.title }
} }
viewState.onInitFilter(sorts, tags, filter) viewState.onInitFilter(sorts, tags, filter)
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -15,7 +15,6 @@ import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.core.prefs.ReaderMode import org.koitharu.kotatsu.core.prefs.ReaderMode
import org.koitharu.kotatsu.domain.MangaDataRepository import org.koitharu.kotatsu.domain.MangaDataRepository
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.MangaUtils import org.koitharu.kotatsu.domain.MangaUtils
import org.koitharu.kotatsu.domain.history.HistoryRepository import org.koitharu.kotatsu.domain.history.HistoryRepository
import org.koitharu.kotatsu.ui.base.BasePresenter import org.koitharu.kotatsu.ui.base.BasePresenter
@@ -35,7 +34,7 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
viewState.onLoadingStateChanged(isLoading = true) viewState.onLoadingStateChanged(isLoading = true)
try { try {
val mode = withContext(Dispatchers.IO) { val mode = withContext(Dispatchers.IO) {
val repo = MangaProviderFactory.create(manga.source) val repo = manga.source.repository
val chapter = val chapter =
(manga.chapters ?: throw RuntimeException("Chapters is null")).random() (manga.chapters ?: throw RuntimeException("Chapters is null")).random()
var mode = dataRepository.getReaderMode(manga.id) var mode = dataRepository.getReaderMode(manga.id)
@@ -77,7 +76,7 @@ class ReaderPresenter : BasePresenter<ReaderView>() {
fun savePage(resolver: ContentResolver, page: MangaPage) { fun savePage(resolver: ContentResolver, page: MangaPage) {
presenterScope.launch(Dispatchers.IO) { presenterScope.launch(Dispatchers.IO) {
try { try {
val repo = MangaProviderFactory.create(page.source) val repo = page.source.repository
val url = repo.getPageFullUrl(page) val url = repo.getPageFullUrl(page)
val request = Request.Builder() val request = Request.Builder()
.url(url) .url(url)

View File

@@ -11,7 +11,6 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.base.BaseFragment import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.PageLoader
import org.koitharu.kotatsu.ui.reader.ReaderListener import org.koitharu.kotatsu.ui.reader.ReaderListener
@@ -124,7 +123,7 @@ abstract class AbstractReader(contentLayoutId: Int) : BaseFragment(contentLayout
val pages = withContext(Dispatchers.IO) { val pages = withContext(Dispatchers.IO) {
val chapter = manga.chapters?.find { it.id == chapterId } val chapter = manga.chapters?.find { it.id == chapterId }
?: throw RuntimeException("Chapter $chapterId not found") ?: throw RuntimeException("Chapter $chapterId not found")
val repo = MangaProviderFactory.create(manga.source) val repo = manga.source.repository
repo.getPages(chapter) repo.getPages(chapter)
} }
callback(pages) callback(pages)

View File

@@ -5,7 +5,6 @@ import androidx.core.net.toUri
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView
import kotlinx.coroutines.* import kotlinx.coroutines.*
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.reader.PageLoader import org.koitharu.kotatsu.ui.reader.PageLoader
import org.koitharu.kotatsu.utils.ext.launchAfter import org.koitharu.kotatsu.utils.ext.launchAfter
import org.koitharu.kotatsu.utils.ext.launchInstead import org.koitharu.kotatsu.utils.ext.launchInstead
@@ -72,7 +71,7 @@ class PageHolderDelegate(
callback.onLoadingStarted() callback.onLoadingStarted()
try { try {
val file = withContext(Dispatchers.IO) { val file = withContext(Dispatchers.IO) {
val pageUrl = MangaProviderFactory.create(data.source).getPageFullUrl(data) val pageUrl = data.source.repository.getPageFullUrl(data)
check(pageUrl.isNotEmpty()) { "Cannot obtain full image url" } check(pageUrl.isNotEmpty()) { "Cannot obtain full image url" }
loader.loadFile(pageUrl, force) loader.loadFile(pageUrl, force)
} }

View File

@@ -12,7 +12,6 @@ import org.koin.core.component.inject
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.local.PagesCache import org.koitharu.kotatsu.core.local.PagesCache
import org.koitharu.kotatsu.core.model.MangaPage import org.koitharu.kotatsu.core.model.MangaPage
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.base.list.BaseViewHolder import org.koitharu.kotatsu.ui.base.list.BaseViewHolder
import org.koitharu.kotatsu.utils.ext.IgnoreErrors import org.koitharu.kotatsu.utils.ext.IgnoreErrors
@@ -37,7 +36,7 @@ class PageThumbnailHolder(parent: ViewGroup, private val scope: CoroutineScope)
job?.cancel() job?.cancel()
job = scope.launch(Dispatchers.IO + IgnoreErrors) { job = scope.launch(Dispatchers.IO + IgnoreErrors) {
val url = data.preview ?: data.url.let { val url = data.preview ?: data.url.let {
val pageUrl = MangaProviderFactory.create(data.source).getPageFullUrl(data) val pageUrl = data.source.repository.getPageFullUrl(data)
extra[pageUrl]?.toUri()?.toString() ?: pageUrl extra[pageUrl]?.toUri()?.toString() ?: pageUrl
} }
val drawable = coil.execute( val drawable = coil.execute(

View File

@@ -4,7 +4,6 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import moxy.InjectViewState import moxy.InjectViewState
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.base.BasePresenter import org.koitharu.kotatsu.ui.base.BasePresenter
import org.koitharu.kotatsu.ui.list.MangaListView import org.koitharu.kotatsu.ui.list.MangaListView
@@ -14,8 +13,7 @@ class SearchPresenter : BasePresenter<MangaListView<Unit>>() {
fun loadList(source: MangaSource, query: String, offset: Int) { fun loadList(source: MangaSource, query: String, offset: Int) {
launchLoadingJob { launchLoadingJob {
val list = withContext(Dispatchers.Default) { val list = withContext(Dispatchers.Default) {
MangaProviderFactory.create(source) source.repository.getList(offset, query = query)
.getList(offset, query = query)
} }
if (offset == 0) { if (offset == 0) {
viewState.onListChanged(list) viewState.onListChanged(list)

View File

@@ -6,7 +6,6 @@ import androidx.preference.PreferenceFragmentCompat
import org.koitharu.kotatsu.R import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.ui.settings.utils.EditTextSummaryProvider import org.koitharu.kotatsu.ui.settings.utils.EditTextSummaryProvider
import org.koitharu.kotatsu.utils.ext.withArgs import org.koitharu.kotatsu.utils.ext.withArgs
@@ -23,7 +22,7 @@ class SourceSettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
preferenceManager.sharedPreferencesName = source.name preferenceManager.sharedPreferencesName = source.name
val repo = MangaProviderFactory.create(source) as? RemoteMangaRepository ?: return val repo = source.repository as? RemoteMangaRepository ?: return
val keys = repo.onCreatePreferences().map(::getString) val keys = repo.onCreatePreferences().map(::getString)
addPreferencesFromResource(R.xml.pref_source) addPreferencesFromResource(R.xml.pref_source)
for (i in 0 until preferenceScreen.preferenceCount) { for (i in 0 until preferenceScreen.preferenceCount) {

View File

@@ -19,7 +19,6 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.Manga import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaChapter import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.domain.tracking.TrackingRepository import org.koitharu.kotatsu.domain.tracking.TrackingRepository
import org.koitharu.kotatsu.ui.details.MangaDetailsActivity import org.koitharu.kotatsu.ui.details.MangaDetailsActivity
import org.koitharu.kotatsu.utils.ext.safe import org.koitharu.kotatsu.utils.ext.safe
@@ -53,8 +52,7 @@ class TrackWorker(context: Context, workerParams: WorkerParameters) :
var success = 0 var success = 0
for (track in tracks) { for (track in tracks) {
val details = safe { val details = safe {
MangaProviderFactory.create(track.manga.source) track.manga.source.repository.getDetails(track.manga)
.getDetails(track.manga)
} }
val chapters = details?.chapters ?: continue val chapters = details?.chapters ?: continue
when { when {

View File

@@ -1,6 +1,7 @@
package org.koitharu.kotatsu.utils.ext package org.koitharu.kotatsu.utils.ext
import android.os.Bundle import android.os.Bundle
import android.os.Parcelable
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.coroutineScope import androidx.lifecycle.coroutineScope
@@ -12,4 +13,11 @@ inline fun <T : Fragment> T.withArgs(size: Int, block: Bundle.() -> Unit): T {
} }
val Fragment.viewLifecycleScope val Fragment.viewLifecycleScope
get() = viewLifecycleOwner.lifecycle.coroutineScope get() = viewLifecycleOwner.lifecycle.coroutineScope
@Suppress("NOTHING_TO_INLINE")
inline fun <T : Parcelable> Fragment.parcelableArgument(name: String) =
lazy<T>(LazyThreadSafetyMode.NONE) {
requireArguments().getParcelable(name)
?: error("No argument $name passed in ${javaClass.simpleName}")
}

View File

@@ -9,18 +9,24 @@ import org.junit.runner.RunWith
import org.junit.runners.Parameterized import org.junit.runners.Parameterized
import org.koin.core.context.startKoin import org.koin.core.context.startKoin
import org.koin.dsl.module import org.koin.dsl.module
import org.koin.test.KoinTest
import org.koin.test.get
import org.koitharu.kotatsu.core.model.MangaSource import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.parser.UserAgentInterceptor import org.koitharu.kotatsu.core.parser.UserAgentInterceptor
import org.koitharu.kotatsu.core.prefs.SourceConfig import org.koitharu.kotatsu.core.prefs.SourceConfig
import org.koitharu.kotatsu.domain.MangaLoaderContext import org.koitharu.kotatsu.domain.MangaLoaderContext
import org.koitharu.kotatsu.domain.MangaProviderFactory
import org.koitharu.kotatsu.utils.AssertX import org.koitharu.kotatsu.utils.AssertX
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@RunWith(Parameterized::class) @RunWith(Parameterized::class)
class RemoteRepositoryTest(source: MangaSource) { class RemoteRepositoryTest(source: MangaSource) : KoinTest {
private val repo = MangaProviderFactory.create(source) private val repo = try {
source.cls.getDeclaredConstructor(MangaLoaderContext::class.java)
.newInstance(get())
} catch (e: NoSuchMethodException) {
source.cls.newInstance()
}
@Test @Test
fun list() { fun list() {