mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-12 20:19:05 +01:00
Initial support for external sources
This commit is contained in:
@@ -14,6 +14,7 @@ import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.data.source.online.LoginSource
|
||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
@@ -32,7 +33,6 @@ import nucleus.factory.RequiresPresenter
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||
|
||||
/**
|
||||
@@ -104,6 +104,11 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
private val toolbar: Toolbar
|
||||
get() = (activity as MainActivity).toolbar
|
||||
|
||||
/**
|
||||
* Snackbar containing an error message when a request fails.
|
||||
*/
|
||||
private var snack: Snackbar? = null
|
||||
|
||||
/**
|
||||
* Navigation view containing filter items.
|
||||
*/
|
||||
@@ -201,8 +206,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
} else if (source != presenter.source) {
|
||||
selectedIndex = position
|
||||
showProgressBar()
|
||||
glm.scrollToPositionWithOffset(0, 0)
|
||||
llm.scrollToPositionWithOffset(0, 0)
|
||||
adapter.clear()
|
||||
presenter.setActiveSource(source)
|
||||
navView?.setFilters(presenter.sourceFilters)
|
||||
activity.invalidateOptionsMenu()
|
||||
@@ -233,14 +237,14 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
}
|
||||
|
||||
navView.onSearchClicked = {
|
||||
val allDefault = (0..navView.adapter.items.lastIndex)
|
||||
.none { navView.adapter.items[it].state != presenter.source.filters[it].state }
|
||||
|
||||
presenter.setSourceFilter(if (allDefault) emptyList() else navView.adapter.items)
|
||||
val allDefault = navView.adapter.items.hasSameState(presenter.source.getFilterList())
|
||||
showProgressBar()
|
||||
adapter.clear()
|
||||
presenter.setSourceFilter(if (allDefault) FilterList() else navView.adapter.items)
|
||||
}
|
||||
|
||||
navView.onResetClicked = {
|
||||
presenter.appliedFilters = emptyList()
|
||||
presenter.appliedFilters = FilterList()
|
||||
val newFilters = presenter.source.getFilterList()
|
||||
presenter.sourceFilters = newFilters
|
||||
navView.setFilters(newFilters)
|
||||
@@ -277,7 +281,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
// Setup filters button
|
||||
menu.findItem(R.id.action_set_filter).apply {
|
||||
icon.mutate()
|
||||
if (presenter.source.filters.isEmpty()) {
|
||||
if (presenter.sourceFilters.isEmpty()) {
|
||||
isEnabled = false
|
||||
icon.alpha = 128
|
||||
} else {
|
||||
@@ -355,8 +359,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
return
|
||||
|
||||
showProgressBar()
|
||||
catalogue_grid.layoutManager.scrollToPosition(0)
|
||||
catalogue_list.layoutManager.scrollToPosition(0)
|
||||
adapter.clear()
|
||||
|
||||
presenter.restartPager(newQuery)
|
||||
}
|
||||
@@ -394,9 +397,11 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
*/
|
||||
fun onAddPageError(error: Throwable) {
|
||||
hideProgressBar()
|
||||
Timber.e(error)
|
||||
|
||||
catalogue_view.snack(error.message ?: "", Snackbar.LENGTH_INDEFINITE) {
|
||||
val message = if (error is NoResultsException) "No results found" else (error.message ?: "")
|
||||
|
||||
snack?.dismiss()
|
||||
snack = catalogue_view.snack(message, Snackbar.LENGTH_INDEFINITE) {
|
||||
setAction(R.string.action_retry) {
|
||||
showProgressBar()
|
||||
presenter.requestNext()
|
||||
@@ -456,6 +461,8 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
*/
|
||||
private fun showProgressBar() {
|
||||
progress.visibility = ProgressBar.VISIBLE
|
||||
snack?.dismiss()
|
||||
snack = null
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -463,6 +470,8 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
|
||||
*/
|
||||
private fun showGridProgressBar() {
|
||||
progress_grid.visibility = ProgressBar.VISIBLE
|
||||
snack?.dismiss()
|
||||
snack = null
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,8 @@ import android.view.ViewGroup
|
||||
import android.widget.ArrayAdapter
|
||||
import android.widget.TextView
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
|
||||
import eu.kanade.tachiyomi.data.source.model.Filter
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.util.dpToPx
|
||||
import eu.kanade.tachiyomi.util.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
@@ -38,14 +39,14 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
|
||||
reset_btn.setOnClickListener { onResetClicked() }
|
||||
}
|
||||
|
||||
fun setFilters(items: List<Filter<*>>) {
|
||||
fun setFilters(items: FilterList) {
|
||||
adapter.items = items
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class Adapter : RecyclerView.Adapter<Holder>() {
|
||||
|
||||
var items: List<Filter<*>> = emptyList()
|
||||
var items: FilterList = FilterList()
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
package eu.kanade.tachiyomi.ui.catalogue
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.data.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
|
||||
open class CataloguePager(val source: OnlineSource, val query: String, val filters: List<Filter<*>>) : Pager() {
|
||||
open class CataloguePager(val source: CatalogueSource, val query: String, val filters: FilterList) : Pager() {
|
||||
|
||||
override fun requestNext(transformer: (Observable<MangasPage>) -> Observable<MangasPage>): Observable<MangasPage> {
|
||||
val lastPage = lastPage
|
||||
|
||||
val page = if (lastPage == null)
|
||||
MangasPage(1)
|
||||
else
|
||||
MangasPage(lastPage.page + 1).apply { url = lastPage.nextPageUrl!! }
|
||||
override fun requestNext(): Observable<MangasPage> {
|
||||
val page = currentPage
|
||||
|
||||
val observable = if (query.isBlank() && filters.isEmpty())
|
||||
source.fetchPopularManga(page)
|
||||
else
|
||||
source.fetchSearchManga(page, query, filters)
|
||||
|
||||
return transformer(observable)
|
||||
.doOnNext { results.onNext(it) }
|
||||
.doOnNext { this@CataloguePager.lastPage = it }
|
||||
return observable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
if (it.mangas.isNotEmpty()) {
|
||||
onPageReceived(it)
|
||||
} else {
|
||||
throw NoResultsException()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,12 +6,12 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.data.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.data.source.Source
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager
|
||||
import eu.kanade.tachiyomi.data.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.data.source.model.SManga
|
||||
import eu.kanade.tachiyomi.data.source.online.LoginSource
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
@@ -55,7 +55,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
/**
|
||||
* Active source.
|
||||
*/
|
||||
lateinit var source: OnlineSource
|
||||
lateinit var source: CatalogueSource
|
||||
private set
|
||||
|
||||
/**
|
||||
@@ -67,12 +67,12 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
/**
|
||||
* Modifiable list of filters.
|
||||
*/
|
||||
var sourceFilters: List<Filter<*>> = emptyList()
|
||||
var sourceFilters = FilterList()
|
||||
|
||||
/**
|
||||
* List of filters used by the [Pager]. If empty alongside [query], the popular query is used.
|
||||
*/
|
||||
var appliedFilters: List<Filter<*>> = emptyList()
|
||||
var appliedFilters = FilterList()
|
||||
|
||||
/**
|
||||
* Pager containing a list of manga results.
|
||||
@@ -136,7 +136,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
* @param query the query.
|
||||
* @param filters the current state of the filters (for search mode).
|
||||
*/
|
||||
fun restartPager(query: String = this.query, filters: List<Filter<*>> = this.appliedFilters) {
|
||||
fun restartPager(query: String = this.query, filters: FilterList = this.appliedFilters) {
|
||||
this.query = query
|
||||
this.appliedFilters = filters
|
||||
|
||||
@@ -145,11 +145,17 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
// Create a new pager.
|
||||
pager = createPager(query, filters)
|
||||
|
||||
val sourceId = source.id
|
||||
|
||||
// Prepare the pager.
|
||||
pagerSubscription?.let { remove(it) }
|
||||
pagerSubscription = pager.results()
|
||||
.subscribeReplay({ view, page ->
|
||||
view.onAddPage(page.page, page.mangas)
|
||||
.observeOn(Schedulers.io())
|
||||
.map { it.first to it.second.map { networkToLocalManga(it, sourceId) } }
|
||||
.doOnNext { initializeMangas(it.second) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeReplay({ view, pair ->
|
||||
view.onAddPage(pair.first, pair.second)
|
||||
}, { view, error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
@@ -165,7 +171,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
if (!hasNextPage()) return
|
||||
|
||||
pageSubscription?.let { remove(it) }
|
||||
pageSubscription = pager.requestNext { getPageTransformer(it) }
|
||||
pageSubscription = Observable.defer { pager.requestNext() }
|
||||
.subscribeFirst({ view, page ->
|
||||
// Nothing to do when onNext is emitted.
|
||||
}, CatalogueFragment::onAddPageError)
|
||||
@@ -175,7 +181,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
* Returns true if the last fetched page has a next page.
|
||||
*/
|
||||
fun hasNextPage(): Boolean {
|
||||
return pager.hasNextPage()
|
||||
return pager.hasNextPage
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,12 +189,12 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
*
|
||||
* @param source the new active source.
|
||||
*/
|
||||
fun setActiveSource(source: OnlineSource) {
|
||||
fun setActiveSource(source: CatalogueSource) {
|
||||
prefs.lastUsedCatalogueSource().set(source.id)
|
||||
this.source = source
|
||||
sourceFilters = source.getFilterList()
|
||||
|
||||
restartPager(query = "", filters = emptyList())
|
||||
restartPager(query = "", filters = FilterList())
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,7 +214,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
initializerSubscription?.let { remove(it) }
|
||||
initializerSubscription = mangaDetailSubject.observeOn(Schedulers.io())
|
||||
.flatMap { Observable.from(it) }
|
||||
.filter { !it.initialized }
|
||||
.filter { it.thumbnail_url == null && !it.initialized }
|
||||
.concatMap { getMangaDetailsObservable(it) }
|
||||
.onBackpressureBuffer()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
@@ -221,41 +227,21 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
.apply { add(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the function to apply to the observable of the list of manga from the source.
|
||||
*
|
||||
* @param observable the observable from the source.
|
||||
* @return the function to apply.
|
||||
*/
|
||||
fun getPageTransformer(observable: Observable<MangasPage>): Observable<MangasPage> {
|
||||
return observable.subscribeOn(Schedulers.io())
|
||||
.doOnNext { it.mangas.replace { networkToLocalManga(it) } }
|
||||
.doOnNext { initializeMangas(it.mangas) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an object in the list with another.
|
||||
*/
|
||||
fun <T> MutableList<T>.replace(block: (T) -> T) {
|
||||
forEachIndexed { i, obj ->
|
||||
set(i, block(obj))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a manga from the database for the given manga from network. It creates a new entry
|
||||
* if the manga is not yet in the database.
|
||||
*
|
||||
* @param networkManga the manga from network.
|
||||
* @param sManga the manga from the source.
|
||||
* @return a manga from the database.
|
||||
*/
|
||||
private fun networkToLocalManga(networkManga: Manga): Manga {
|
||||
var localManga = db.getManga(networkManga.url, source.id).executeAsBlocking()
|
||||
private fun networkToLocalManga(sManga: SManga, sourceId: Long): Manga {
|
||||
var localManga = db.getManga(sManga.url, sourceId).executeAsBlocking()
|
||||
if (localManga == null) {
|
||||
val result = db.insertManga(networkManga).executeAsBlocking()
|
||||
networkManga.id = result.insertedId()
|
||||
localManga = networkManga
|
||||
val newManga = Manga.create(sManga.url, sManga.title, sourceId)
|
||||
newManga.copyFrom(sManga)
|
||||
val result = db.insertManga(newManga).executeAsBlocking()
|
||||
newManga.id = result.insertedId()
|
||||
localManga = newManga
|
||||
}
|
||||
return localManga
|
||||
}
|
||||
@@ -279,6 +265,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
return source.fetchMangaDetails(manga)
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just(manga)
|
||||
}
|
||||
@@ -290,13 +277,13 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
*
|
||||
* @return a source.
|
||||
*/
|
||||
fun getLastUsedSource(): OnlineSource {
|
||||
fun getLastUsedSource(): CatalogueSource {
|
||||
val id = prefs.lastUsedCatalogueSource().get() ?: -1
|
||||
val source = sourceManager.get(id)
|
||||
if (!isValidSource(source)) {
|
||||
return findFirstValidSource()
|
||||
}
|
||||
return source as OnlineSource
|
||||
return source as CatalogueSource
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,14 +307,14 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
*
|
||||
* @return the index of the first valid source.
|
||||
*/
|
||||
fun findFirstValidSource(): OnlineSource {
|
||||
fun findFirstValidSource(): CatalogueSource {
|
||||
return sources.first { isValidSource(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of enabled sources ordered by language and name.
|
||||
*/
|
||||
open protected fun getEnabledSources(): List<OnlineSource> {
|
||||
open protected fun getEnabledSources(): List<CatalogueSource> {
|
||||
val languages = prefs.enabledLanguages().getOrDefault()
|
||||
val hiddenCatalogues = prefs.hiddenCatalogues().getOrDefault()
|
||||
|
||||
@@ -336,7 +323,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
languages.add("en")
|
||||
}
|
||||
|
||||
return sourceManager.getOnlineSources()
|
||||
return sourceManager.getCatalogueSources()
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedBy { "(${it.lang}) ${it.name}" }
|
||||
@@ -365,13 +352,13 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
||||
/**
|
||||
* Set the filter states for the current source.
|
||||
*
|
||||
* @param filterStates a list of active filters.
|
||||
* @param filters a list of active filters.
|
||||
*/
|
||||
fun setSourceFilter(filters: List<Filter<*>>) {
|
||||
fun setSourceFilter(filters: FilterList) {
|
||||
restartPager(filters = filters)
|
||||
}
|
||||
|
||||
open fun createPager(query: String, filters: List<Filter<*>>): Pager {
|
||||
open fun createPager(query: String, filters: FilterList): Pager {
|
||||
return CataloguePager(source, query, filters)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package eu.kanade.tachiyomi.ui.catalogue
|
||||
|
||||
class NoResultsException : Exception()
|
||||
@@ -1,25 +1,31 @@
|
||||
package eu.kanade.tachiyomi.ui.catalogue
|
||||
|
||||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.tachiyomi.data.source.model.MangasPage
|
||||
import rx.subjects.PublishSubject
|
||||
import eu.kanade.tachiyomi.data.source.model.SManga
|
||||
import rx.Observable
|
||||
|
||||
/**
|
||||
* A general pager for source requests (latest updates, popular, search)
|
||||
*/
|
||||
abstract class Pager {
|
||||
abstract class Pager(var currentPage: Int = 1) {
|
||||
|
||||
protected var lastPage: MangasPage? = null
|
||||
var hasNextPage = true
|
||||
private set
|
||||
|
||||
protected val results = PublishSubject.create<MangasPage>()
|
||||
protected val results: PublishRelay<Pair<Int, List<SManga>>> = PublishRelay.create()
|
||||
|
||||
fun results(): Observable<MangasPage> {
|
||||
fun results(): Observable<Pair<Int, List<SManga>>> {
|
||||
return results.asObservable()
|
||||
}
|
||||
|
||||
fun hasNextPage(): Boolean {
|
||||
return lastPage == null || lastPage?.nextPageUrl != null
|
||||
abstract fun requestNext(): Observable<MangasPage>
|
||||
|
||||
fun onPageReceived(mangasPage: MangasPage) {
|
||||
val page = currentPage
|
||||
currentPage++
|
||||
hasNextPage = mangasPage.hasNextPage && !mangasPage.mangas.isEmpty()
|
||||
results.call(Pair(page, mangasPage.mangas))
|
||||
}
|
||||
|
||||
abstract fun requestNext(transformer: (Observable<MangasPage>) -> Observable<MangasPage>): Observable<MangasPage>
|
||||
}
|
||||
@@ -1,28 +1,22 @@
|
||||
package eu.kanade.tachiyomi.ui.latest_updates
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.data.source.model.MangasPage
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.ui.catalogue.Pager
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
|
||||
/**
|
||||
* LatestUpdatesPager inherited from the general Pager.
|
||||
*/
|
||||
class LatestUpdatesPager(val source: OnlineSource): Pager() {
|
||||
class LatestUpdatesPager(val source: CatalogueSource): Pager() {
|
||||
|
||||
override fun requestNext(transformer: (Observable<MangasPage>) -> Observable<MangasPage>): Observable<MangasPage> {
|
||||
val lastPage = lastPage
|
||||
|
||||
val page = if (lastPage == null)
|
||||
MangasPage(1)
|
||||
else
|
||||
MangasPage(lastPage.page + 1).apply { url = lastPage.nextPageUrl!! }
|
||||
|
||||
val observable = source.fetchLatestUpdates(page)
|
||||
|
||||
return transformer(observable)
|
||||
.doOnNext { results.onNext(it) }
|
||||
.doOnNext { this@LatestUpdatesPager.lastPage = it }
|
||||
override fun requestNext(): Observable<MangasPage> {
|
||||
return source.fetchLatestUpdates(currentPage)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { onPageReceived(it) }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
package eu.kanade.tachiyomi.ui.latest_updates
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.CatalogueSource
|
||||
import eu.kanade.tachiyomi.data.source.Source
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.data.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CataloguePresenter
|
||||
import eu.kanade.tachiyomi.ui.catalogue.Pager
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
|
||||
|
||||
/**
|
||||
* Presenter of [LatestUpdatesFragment]. Inherit CataloguePresenter.
|
||||
*/
|
||||
class LatestUpdatesPresenter : CataloguePresenter() {
|
||||
|
||||
override fun createPager(query: String, filters: List<Filter<*>>): Pager {
|
||||
override fun createPager(query: String, filters: FilterList): Pager {
|
||||
return LatestUpdatesPager(source)
|
||||
}
|
||||
|
||||
override fun getEnabledSources(): List<OnlineSource> {
|
||||
override fun getEnabledSources(): List<CatalogueSource> {
|
||||
return super.getEnabledSources().filter { it.supportsLatest }
|
||||
}
|
||||
|
||||
override fun isValidSource(source: Source?): Boolean {
|
||||
return super.isValidSource(source) && (source as OnlineSource).supportsLatest
|
||||
return super.isValidSource(source) && (source as CatalogueSource).supportsLatest
|
||||
}
|
||||
|
||||
}
|
||||
@@ -125,7 +125,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
|
||||
*/
|
||||
private fun applyFilters(map: Map<Int, List<Manga>>): Map<Int, List<Manga>> {
|
||||
// Cached list of downloaded manga directories given a source id.
|
||||
val mangaDirectories = mutableMapOf<Int, Array<UniFile>>()
|
||||
val mangaDirectories = mutableMapOf<Long, Array<UniFile>>()
|
||||
|
||||
// Cached list of downloaded chapter directories for a manga.
|
||||
val chapterDirectories = mutableMapOf<Long, Boolean>()
|
||||
|
||||
@@ -197,7 +197,7 @@ class ChaptersPresenter : BasePresenter<ChaptersFragment>() {
|
||||
/**
|
||||
* Returns an observable that updates the chapter list with the latest from the source.
|
||||
*/
|
||||
fun getRemoteChaptersObservable() = source.fetchChapterList(manga)
|
||||
fun getRemoteChaptersObservable() = Observable.defer { source.fetchChapterList(manga) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.map { syncChaptersWithSource(db, it, manga, source) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.source.Source
|
||||
import eu.kanade.tachiyomi.data.source.model.SManga
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||
@@ -122,9 +123,9 @@ class MangaInfoFragment : BaseRxFragment<MangaInfoPresenter>() {
|
||||
|
||||
// Update status TextView.
|
||||
manga_status.setText(when (manga.status) {
|
||||
Manga.ONGOING -> R.string.ongoing
|
||||
Manga.COMPLETED -> R.string.completed
|
||||
Manga.LICENSED -> R.string.licensed
|
||||
SManga.ONGOING -> R.string.ongoing
|
||||
SManga.COMPLETED -> R.string.completed
|
||||
SManga.LICENSED -> R.string.licensed
|
||||
else -> R.string.unknown
|
||||
})
|
||||
|
||||
|
||||
@@ -99,9 +99,10 @@ class MangaInfoPresenter : BasePresenter<MangaInfoFragment>() {
|
||||
* @return manga information.
|
||||
*/
|
||||
private fun fetchMangaObs(): Observable<Manga> {
|
||||
return source.fetchMangaDetails(manga)
|
||||
return Observable.defer { source.fetchMangaDetails(manga) }
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just<Manga>(manga)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.source.Source
|
||||
import eu.kanade.tachiyomi.data.source.model.Page
|
||||
import eu.kanade.tachiyomi.data.source.online.OnlineSource
|
||||
import eu.kanade.tachiyomi.data.source.online.fetchImageFromCacheThenNet
|
||||
import eu.kanade.tachiyomi.data.source.online.fetchPageListFromCacheThenNet
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import rx.Observable
|
||||
import rx.schedulers.Schedulers
|
||||
@@ -36,9 +39,11 @@ class ChapterLoader(
|
||||
}
|
||||
|
||||
private fun prepareOnlineReading() {
|
||||
if (source !is OnlineSource) return
|
||||
|
||||
subscriptions += Observable.defer { Observable.just(queue.take().page) }
|
||||
.filter { it.status == Page.QUEUE }
|
||||
.concatMap { source.fetchImage(it) }
|
||||
.concatMap { source.fetchImageFromCacheThenNet(it) }
|
||||
.repeat()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({
|
||||
@@ -57,6 +62,10 @@ class ChapterLoader(
|
||||
Observable.just(chapter.pages!!)
|
||||
}
|
||||
.doOnNext { pages ->
|
||||
if (pages.isEmpty()) {
|
||||
throw Exception("Page list is empty")
|
||||
}
|
||||
|
||||
// Now that the number of pages is known, fix the requested page if the last one
|
||||
// was requested.
|
||||
if (chapter.requestedPage == -1) {
|
||||
@@ -76,8 +85,8 @@ class ChapterLoader(
|
||||
// Fetch the page list from disk.
|
||||
downloadManager.buildPageList(source, manga, chapter)
|
||||
} else {
|
||||
// Fetch the page list from cache or fallback to network
|
||||
source.fetchPageList(chapter)
|
||||
(source as? OnlineSource)?.fetchPageListFromCacheThenNet(chapter)
|
||||
?: source.fetchPageList(chapter)
|
||||
}
|
||||
}
|
||||
.doOnNext { pages ->
|
||||
@@ -111,6 +120,8 @@ class ChapterLoader(
|
||||
queue.offer(PriorityPage(page, 2))
|
||||
}
|
||||
|
||||
|
||||
|
||||
private data class PriorityPage(val page: Page, val priority: Int): Comparable<PriorityPage> {
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -372,7 +372,9 @@ class ReaderPresenter : BasePresenter<ReaderActivity>() {
|
||||
Observable.fromCallable {
|
||||
// Cache current page list progress for online chapters to allow a faster reopen
|
||||
if (!chapter.isDownloaded) {
|
||||
source.let { if (it is OnlineSource) it.savePageList(chapter, pages) }
|
||||
source.let {
|
||||
if (it is OnlineSource) chapterCache.putPageListToCache(chapter, pages)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -130,7 +130,7 @@ class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
|
||||
*/
|
||||
private fun setDownloadedChapters(chapters: List<RecentChapter>) {
|
||||
// Cached list of downloaded manga directories.
|
||||
val mangaDirectories = mutableMapOf<Int, Array<UniFile>>()
|
||||
val mangaDirectories = mutableMapOf<Long, Array<UniFile>>()
|
||||
|
||||
// Cached list of downloaded chapter directories for a manga.
|
||||
val chapterDirectories = mutableMapOf<Long, Array<UniFile>>()
|
||||
|
||||
@@ -123,13 +123,14 @@ class SettingsSourcesFragment : SettingsFragment() {
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == SOURCE_CHANGE_REQUEST) {
|
||||
val pref = findPreference(getSourceKey(resultCode)) as? LoginCheckBoxPreference
|
||||
if (requestCode == SOURCE_CHANGE_REQUEST && data != null) {
|
||||
val sourceId = data.getLongExtra("key", -1L)
|
||||
val pref = findPreference(getSourceKey(sourceId)) as? LoginCheckBoxPreference
|
||||
pref?.notifyChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSourceKey(sourceId: Int): String {
|
||||
private fun getSourceKey(sourceId: Long): String {
|
||||
return "source_$sourceId"
|
||||
}
|
||||
|
||||
|
||||
@@ -81,8 +81,9 @@ class SettingsTrackingFragment : SettingsFragment() {
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode == SYNC_CHANGE_REQUEST) {
|
||||
updatePreference(resultCode)
|
||||
if (requestCode == SYNC_CHANGE_REQUEST && data != null) {
|
||||
val serviceId = data.getIntExtra("key", -1)
|
||||
updatePreference(serviceId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user