Linting fixes
This commit is contained in:
@@ -7,7 +7,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import kotlinx.android.synthetic.main.source_main_controller_card.title
|
||||
|
||||
class LangHolder(view: View, adapter: FlexibleAdapter<*>) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
fun bind(item: LangItem) {
|
||||
title.text = LocaleHelper.getSourceDisplayName(item.code, itemView.context)
|
||||
|
||||
@@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
* @param controller instance of [SourceController].
|
||||
*/
|
||||
class SourceAdapter(val controller: SourceController) :
|
||||
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
|
||||
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
|
||||
|
||||
val cardBackground = controller.activity!!.getResourceColor(R.attr.colorSurface)
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsSourcesController
|
||||
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
|
||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.source.latest.LatestUpdatesController
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -44,12 +44,13 @@ import uy.kohesive.injekt.api.get
|
||||
* [SourceAdapter.OnBrowseClickListener] call function data on browse item click.
|
||||
* [SourceAdapter.OnLatestClickListener] call function data on latest item click
|
||||
*/
|
||||
class SourceController : NucleusController<SourceMainControllerBinding, SourcePresenter>(),
|
||||
RootController,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
SourceAdapter.OnBrowseClickListener,
|
||||
SourceAdapter.OnLatestClickListener {
|
||||
class SourceController :
|
||||
NucleusController<SourceMainControllerBinding, SourcePresenter>(),
|
||||
RootController,
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
SourceAdapter.OnBrowseClickListener,
|
||||
SourceAdapter.OnLatestClickListener {
|
||||
|
||||
private val preferences: PreferencesHelper = Injekt.get()
|
||||
|
||||
@@ -122,21 +123,21 @@ class SourceController : NucleusController<SourceMainControllerBinding, SourcePr
|
||||
val isPinned = item.header?.code?.equals(SourcePresenter.PINNED_KEY) ?: false
|
||||
|
||||
MaterialDialog(activity)
|
||||
.title(text = item.source.name)
|
||||
.listItems(
|
||||
items = listOf(
|
||||
activity.getString(R.string.action_hide),
|
||||
activity.getString(if (isPinned) R.string.action_unpin else R.string.action_pin)
|
||||
),
|
||||
waitForPositiveButton = false
|
||||
) { dialog, which, _ ->
|
||||
when (which) {
|
||||
0 -> hideCatalogue(item.source)
|
||||
1 -> pinCatalogue(item.source, isPinned)
|
||||
}
|
||||
dialog.dismiss()
|
||||
.title(text = item.source.name)
|
||||
.listItems(
|
||||
items = listOf(
|
||||
activity.getString(R.string.action_hide),
|
||||
activity.getString(if (isPinned) R.string.action_unpin else R.string.action_pin)
|
||||
),
|
||||
waitForPositiveButton = false
|
||||
) { dialog, which, _ ->
|
||||
when (which) {
|
||||
0 -> hideCatalogue(item.source)
|
||||
1 -> pinCatalogue(item.source, isPinned)
|
||||
}
|
||||
.show()
|
||||
dialog.dismiss()
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun hideCatalogue(source: Source) {
|
||||
@@ -218,9 +219,11 @@ class SourceController : NucleusController<SourceMainControllerBinding, SourcePr
|
||||
when (item.itemId) {
|
||||
// Initialize option to open catalogue settings.
|
||||
R.id.action_settings -> {
|
||||
router.pushController((RouterTransaction.with(SettingsSourcesController()))
|
||||
router.pushController(
|
||||
(RouterTransaction.with(SettingsSourcesController()))
|
||||
.popChangeHandler(SettingsSourcesFadeChangeHandler())
|
||||
.pushChangeHandler(FadeChangeHandler()))
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
)
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
|
||||
@@ -23,7 +23,8 @@ class SourceDividerItemDecoration(context: Context) : RecyclerView.ItemDecoratio
|
||||
val child = parent.getChildAt(i)
|
||||
val holder = parent.getChildViewHolder(child)
|
||||
if (holder is SourceHolder &&
|
||||
parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder) {
|
||||
parent.getChildViewHolder(parent.getChildAt(i + 1)) is SourceHolder
|
||||
) {
|
||||
val params = child.layoutParams as RecyclerView.LayoutParams
|
||||
val top = child.bottom + params.bottomMargin
|
||||
val bottom = top + divider.intrinsicHeight
|
||||
|
||||
@@ -16,8 +16,8 @@ import kotlinx.android.synthetic.main.source_main_controller_card_item.source_la
|
||||
import kotlinx.android.synthetic.main.source_main_controller_card_item.title
|
||||
|
||||
class SourceHolder(view: View, override val adapter: SourceAdapter) :
|
||||
BaseFlexibleViewHolder(view, adapter),
|
||||
SlicedHolder {
|
||||
BaseFlexibleViewHolder(view, adapter),
|
||||
SlicedHolder {
|
||||
|
||||
override val slice = Slice(card).apply {
|
||||
setColor(adapter.cardBackground)
|
||||
|
||||
@@ -15,7 +15,7 @@ import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
* @param header The header for this item.
|
||||
*/
|
||||
data class SourceItem(val source: CatalogueSource, val header: LangItem? = null) :
|
||||
AbstractSectionableItem<SourceHolder, LangItem>(header) {
|
||||
AbstractSectionableItem<SourceHolder, LangItem>(header) {
|
||||
|
||||
/**
|
||||
* Returns the layout resource of this item.
|
||||
|
||||
@@ -75,7 +75,7 @@ class SourcePresenter(
|
||||
}
|
||||
|
||||
sourceSubscription = Observable.just(sourceItems)
|
||||
.subscribeLatestCache(SourceController::setSources)
|
||||
.subscribeLatestCache(SourceController::setSources)
|
||||
}
|
||||
|
||||
private fun loadLastUsedSource() {
|
||||
@@ -83,11 +83,12 @@ class SourcePresenter(
|
||||
|
||||
// Emit the first item immediately but delay subsequent emissions by 500ms.
|
||||
Observable.merge(
|
||||
sharedObs.take(1),
|
||||
sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()))
|
||||
.distinctUntilChanged()
|
||||
.map { item -> (sourceManager.get(item) as? CatalogueSource)?.let { SourceItem(it) } }
|
||||
.subscribeLatestCache(SourceController::setLastUsedSource)
|
||||
sharedObs.take(1),
|
||||
sharedObs.skip(1).delay(500, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.map { item -> (sourceManager.get(item) as? CatalogueSource)?.let { SourceItem(it) } }
|
||||
.subscribeLatestCache(SourceController::setLastUsedSource)
|
||||
}
|
||||
|
||||
fun updateSources() {
|
||||
@@ -105,10 +106,10 @@ class SourcePresenter(
|
||||
val hiddenCatalogues = preferences.hiddenCatalogues().get()
|
||||
|
||||
return sourceManager.getCatalogueSources()
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedBy { "(${it.lang}) ${it.name}" } +
|
||||
sourceManager.get(LocalSource.ID) as LocalSource
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedBy { "(${it.lang}) ${it.name}" } +
|
||||
sourceManager.get(LocalSource.ID) as LocalSource
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -56,15 +56,17 @@ import uy.kohesive.injekt.injectLazy
|
||||
* Controller to manage the catalogues available in the app.
|
||||
*/
|
||||
open class BrowseSourceController(bundle: Bundle) :
|
||||
NucleusController<SourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.EndlessScrollListener,
|
||||
ChangeMangaCategoriesDialog.Listener {
|
||||
NucleusController<SourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.EndlessScrollListener,
|
||||
ChangeMangaCategoriesDialog.Listener {
|
||||
|
||||
constructor(source: CatalogueSource) : this(Bundle().apply {
|
||||
putLong(SOURCE_ID_KEY, source.id)
|
||||
})
|
||||
constructor(source: CatalogueSource) : this(
|
||||
Bundle().apply {
|
||||
putLong(SOURCE_ID_KEY, source.id)
|
||||
}
|
||||
)
|
||||
|
||||
private val preferences: PreferencesHelper by injectLazy()
|
||||
|
||||
@@ -185,10 +187,10 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
} else {
|
||||
(binding.catalogueView.inflate(R.layout.source_recycler_autofit) as AutofitRecyclerView).apply {
|
||||
numColumnsSubscription = getColumnsPreferenceForCurrentOrientation().asObservable()
|
||||
.doOnNext { spanCount = it }
|
||||
.skip(1)
|
||||
// Set again the adapter to recalculate the covers height
|
||||
.subscribe { adapter = this@BrowseSourceController.adapter }
|
||||
.doOnNext { spanCount = it }
|
||||
.skip(1)
|
||||
// Set again the adapter to recalculate the covers height
|
||||
.subscribe { adapter = this@BrowseSourceController.adapter }
|
||||
|
||||
(layoutManager as GridLayoutManager).spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
@@ -246,19 +248,20 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
.launchIn(scope)
|
||||
|
||||
searchItem.fixExpand(
|
||||
onExpand = { invalidateMenuOnExpand() },
|
||||
onCollapse = {
|
||||
searchWithQuery("")
|
||||
true
|
||||
}
|
||||
onExpand = { invalidateMenuOnExpand() },
|
||||
onCollapse = {
|
||||
searchWithQuery("")
|
||||
true
|
||||
}
|
||||
)
|
||||
|
||||
// Show next display mode
|
||||
menu.findItem(R.id.action_display_mode).apply {
|
||||
val icon = if (presenter.isListMode)
|
||||
val icon = if (presenter.isListMode) {
|
||||
R.drawable.ic_view_module_24dp
|
||||
else
|
||||
} else {
|
||||
R.drawable.ic_view_list_24dp
|
||||
}
|
||||
setIcon(icon)
|
||||
}
|
||||
}
|
||||
@@ -302,8 +305,9 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
*/
|
||||
fun searchWithQuery(newQuery: String) {
|
||||
// If text didn't change, do nothing
|
||||
if (presenter.query == newQuery)
|
||||
if (presenter.query == newQuery) {
|
||||
return
|
||||
}
|
||||
|
||||
showProgressBar()
|
||||
adapter?.clear()
|
||||
@@ -342,7 +346,6 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
|
||||
val message = getErrorMessage(error)
|
||||
val retryAction = View.OnClickListener {
|
||||
|
||||
// If not the first page, show bottom progress bar.
|
||||
if (adapter.mainItemCount > 0 && progressItem != null) {
|
||||
adapter.addScrollableFooterWithDelay(progressItem!!, 0, true)
|
||||
@@ -444,10 +447,11 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
* @return the preference.
|
||||
*/
|
||||
fun getColumnsPreferenceForCurrentOrientation(): Preference<Int> {
|
||||
return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT)
|
||||
return if (resources?.configuration?.orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
preferences.portraitColumns()
|
||||
else
|
||||
} else {
|
||||
preferences.landscapeColumns()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -514,19 +518,19 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
|
||||
if (manga.favorite) {
|
||||
MaterialDialog(activity)
|
||||
.listItems(
|
||||
items = listOf(activity.getString(R.string.remove_from_library)),
|
||||
waitForPositiveButton = false
|
||||
) { _, which, _ ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_removed_library))
|
||||
}
|
||||
.listItems(
|
||||
items = listOf(activity.getString(R.string.remove_from_library)),
|
||||
waitForPositiveButton = false
|
||||
) { _, which, _ ->
|
||||
when (which) {
|
||||
0 -> {
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
activity.toast(activity.getString(R.string.manga_removed_library))
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
@@ -559,7 +563,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
}.toTypedArray()
|
||||
|
||||
ChangeMangaCategoriesDialog(this, listOf(manga), categories, preselected)
|
||||
.showDialog(router)
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,8 +115,10 @@ open class BrowseSourcePresenter(
|
||||
query = savedState.getString(::query.name, "")
|
||||
}
|
||||
|
||||
add(prefs.catalogueAsList().asObservable()
|
||||
.subscribe { setDisplayMode(it) })
|
||||
add(
|
||||
prefs.catalogueAsList().asObservable()
|
||||
.subscribe { setDisplayMode(it) }
|
||||
)
|
||||
|
||||
restartPager()
|
||||
}
|
||||
@@ -148,16 +150,19 @@ open class BrowseSourcePresenter(
|
||||
// Prepare the pager.
|
||||
pagerSubscription?.let { remove(it) }
|
||||
pagerSubscription = pager.results()
|
||||
.observeOn(Schedulers.io())
|
||||
.map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } }
|
||||
.doOnNext { initializeMangas(it.second) }
|
||||
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueAsList) } }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeReplay({ view, (page, mangas) ->
|
||||
.observeOn(Schedulers.io())
|
||||
.map { pair -> pair.first to pair.second.map { networkToLocalManga(it, sourceId) } }
|
||||
.doOnNext { initializeMangas(it.second) }
|
||||
.map { pair -> pair.first to pair.second.map { SourceItem(it, catalogueAsList) } }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeReplay(
|
||||
{ view, (page, mangas) ->
|
||||
view.onAddPage(page, mangas)
|
||||
}, { _, error ->
|
||||
},
|
||||
{ _, error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// Request first page.
|
||||
requestNext()
|
||||
@@ -171,9 +176,12 @@ open class BrowseSourcePresenter(
|
||||
|
||||
pageSubscription?.let { remove(it) }
|
||||
pageSubscription = Observable.defer { pager.requestNext() }
|
||||
.subscribeFirst({ _, _ ->
|
||||
.subscribeFirst(
|
||||
{ _, _ ->
|
||||
// Nothing to do when onNext is emitted.
|
||||
}, BrowseSourceController::onAddPageError)
|
||||
},
|
||||
BrowseSourceController::onAddPageError
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -199,18 +207,21 @@ open class BrowseSourcePresenter(
|
||||
private fun subscribeToMangaInitializer() {
|
||||
initializerSubscription?.let { remove(it) }
|
||||
initializerSubscription = mangaDetailSubject.observeOn(Schedulers.io())
|
||||
.flatMap { Observable.from(it) }
|
||||
.filter { it.thumbnail_url == null && !it.initialized }
|
||||
.concatMap { getMangaDetailsObservable(it) }
|
||||
.onBackpressureBuffer()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ manga ->
|
||||
.flatMap { Observable.from(it) }
|
||||
.filter { it.thumbnail_url == null && !it.initialized }
|
||||
.concatMap { getMangaDetailsObservable(it) }
|
||||
.onBackpressureBuffer()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ manga ->
|
||||
@Suppress("DEPRECATION")
|
||||
view?.onMangaInitialized(manga)
|
||||
}, { error ->
|
||||
},
|
||||
{ error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
.apply { add(this) }
|
||||
}
|
||||
)
|
||||
.apply { add(this) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,13 +260,13 @@ open class BrowseSourcePresenter(
|
||||
*/
|
||||
private fun getMangaDetailsObservable(manga: Manga): Observable<Manga> {
|
||||
return source.fetchMangaDetails(manga)
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just(manga)
|
||||
}
|
||||
.onErrorResumeNext { Observable.just(manga) }
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just(manga)
|
||||
}
|
||||
.onErrorResumeNext { Observable.just(manga) }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,7 +20,7 @@ import kotlinx.android.synthetic.main.source_grid_item.title
|
||||
* @constructor creates a new catalogue holder.
|
||||
*/
|
||||
class SourceGridHolder(private val view: View, private val adapter: FlexibleAdapter<*>) :
|
||||
SourceHolder(view, adapter) {
|
||||
SourceHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||
@@ -42,11 +42,11 @@ class SourceGridHolder(private val view: View, private val adapter: FlexibleAdap
|
||||
GlideApp.with(view.context).clear(thumbnail)
|
||||
if (!manga.thumbnail_url.isNullOrEmpty()) {
|
||||
GlideApp.with(view.context)
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(StateImageViewTarget(thumbnail, progress))
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(StateImageViewTarget(thumbnail, progress))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
* @param adapter the adapter handling this holder.
|
||||
*/
|
||||
abstract class SourceHolder(view: View, adapter: FlexibleAdapter<*>) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
* Method called from [CatalogueAdapter.onBindViewHolder]. It updates the data for this
|
||||
|
||||
@@ -17,13 +17,14 @@ import kotlinx.android.synthetic.main.source_grid_item.view.card
|
||||
import kotlinx.android.synthetic.main.source_grid_item.view.gradient
|
||||
|
||||
class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boolean>) :
|
||||
AbstractFlexibleItem<SourceHolder>() {
|
||||
AbstractFlexibleItem<SourceHolder>() {
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return if (catalogueAsList.getOrDefault())
|
||||
return if (catalogueAsList.getOrDefault()) {
|
||||
R.layout.source_list_item
|
||||
else
|
||||
} else {
|
||||
R.layout.source_grid_item
|
||||
}
|
||||
}
|
||||
|
||||
override fun createViewHolder(
|
||||
@@ -34,9 +35,11 @@ class SourceItem(val manga: Manga, private val catalogueAsList: Preference<Boole
|
||||
return if (parent is AutofitRecyclerView) {
|
||||
view.apply {
|
||||
card.layoutParams = FrameLayout.LayoutParams(
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4)
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4
|
||||
)
|
||||
gradient.layoutParams = FrameLayout.LayoutParams(
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM)
|
||||
MATCH_PARENT, parent.itemWidth / 3 * 4 / 2, Gravity.BOTTOM
|
||||
)
|
||||
}
|
||||
SourceGridHolder(view, adapter)
|
||||
} else {
|
||||
|
||||
@@ -21,7 +21,7 @@ import kotlinx.android.synthetic.main.source_list_item.title
|
||||
* @constructor creates a new catalogue holder.
|
||||
*/
|
||||
class SourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
|
||||
SourceHolder(view, adapter) {
|
||||
SourceHolder(view, adapter) {
|
||||
|
||||
private val favoriteColor = ContextCompat.getColor(view.context, R.color.material_on_surface_disabled)
|
||||
private val unfavoriteColor = view.context.getResourceColor(R.attr.colorOnSurface)
|
||||
@@ -43,13 +43,13 @@ class SourceListHolder(private val view: View, adapter: FlexibleAdapter<*>) :
|
||||
GlideApp.with(view.context).clear(thumbnail)
|
||||
if (!manga.thumbnail_url.isNullOrEmpty()) {
|
||||
GlideApp.with(view.context)
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.circleCrop()
|
||||
.dontAnimate()
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(thumbnail)
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.circleCrop()
|
||||
.dontAnimate()
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(thumbnail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,20 +12,21 @@ open class SourcePager(val source: CatalogueSource, val query: String, val filte
|
||||
override fun requestNext(): Observable<MangasPage> {
|
||||
val page = currentPage
|
||||
|
||||
val observable = if (query.isBlank() && filters.isEmpty())
|
||||
val observable = if (query.isBlank() && filters.isEmpty()) {
|
||||
source.fetchPopularManga(page)
|
||||
else
|
||||
} else {
|
||||
source.fetchSearchManga(page, query, filters)
|
||||
}
|
||||
|
||||
return observable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
if (it.mangas.isNotEmpty()) {
|
||||
onPageReceived(it)
|
||||
} else {
|
||||
throw NoResultsException()
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext {
|
||||
if (it.mangas.isNotEmpty()) {
|
||||
onPageReceived(it)
|
||||
} else {
|
||||
throw NoResultsException()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,10 +34,13 @@ class GroupItem(val filter: Filter.Group<*>) : AbstractExpandableHeaderItem<Grou
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
|
||||
holder.title.text = filter.name
|
||||
|
||||
holder.icon.setVectorCompat(if (isExpanded)
|
||||
R.drawable.ic_expand_more_white_24dp
|
||||
else
|
||||
R.drawable.ic_chevron_right_white_24dp)
|
||||
holder.icon.setVectorCompat(
|
||||
if (isExpanded) {
|
||||
R.drawable.ic_expand_more_white_24dp
|
||||
} else {
|
||||
R.drawable.ic_chevron_right_white_24dp
|
||||
}
|
||||
)
|
||||
|
||||
holder.itemView.setOnClickListener(holder)
|
||||
}
|
||||
|
||||
@@ -28,8 +28,10 @@ open class SelectItem(val filter: Filter.Select<*>) : AbstractFlexibleItem<Selec
|
||||
|
||||
val spinner = holder.spinner
|
||||
spinner.prompt = filter.name
|
||||
spinner.adapter = ArrayAdapter<Any>(holder.itemView.context,
|
||||
android.R.layout.simple_spinner_item, filter.values).apply {
|
||||
spinner.adapter = ArrayAdapter<Any>(
|
||||
holder.itemView.context,
|
||||
android.R.layout.simple_spinner_item, filter.values
|
||||
).apply {
|
||||
setDropDownViewResource(R.layout.common_spinner_item)
|
||||
}
|
||||
spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { pos ->
|
||||
|
||||
@@ -31,10 +31,13 @@ class SortGroup(val filter: Filter.Sort) : AbstractExpandableHeaderItem<SortGrou
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: Holder, position: Int, payloads: List<Any?>?) {
|
||||
holder.title.text = filter.name
|
||||
|
||||
holder.icon.setVectorCompat(if (isExpanded)
|
||||
R.drawable.ic_expand_more_white_24dp
|
||||
else
|
||||
R.drawable.ic_chevron_right_white_24dp)
|
||||
holder.icon.setVectorCompat(
|
||||
if (isExpanded) {
|
||||
R.drawable.ic_expand_more_white_24dp
|
||||
} else {
|
||||
R.drawable.ic_chevron_right_white_24dp
|
||||
}
|
||||
)
|
||||
|
||||
holder.itemView.setOnClickListener(holder)
|
||||
}
|
||||
|
||||
@@ -35,9 +35,11 @@ class SortItem(val name: String, val group: SortGroup) : AbstractSectionableItem
|
||||
val i = filter.values.indexOf(name)
|
||||
|
||||
fun getIcon() = when (filter.state) {
|
||||
Filter.Sort.Selection(i, false) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_down_white_32dp, null)
|
||||
Filter.Sort.Selection(i, false) ->
|
||||
VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_down_white_32dp, null)
|
||||
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
|
||||
Filter.Sort.Selection(i, true) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_up_white_32dp, null)
|
||||
Filter.Sort.Selection(i, true) ->
|
||||
VectorDrawableCompat.create(view.resources, R.drawable.ic_arrow_up_white_32dp, null)
|
||||
?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
|
||||
else -> ContextCompat.getDrawable(view.context, R.drawable.empty_drawable_32dp)
|
||||
}
|
||||
|
||||
@@ -33,16 +33,21 @@ open class TriStateItem(val filter: Filter.TriState) : AbstractFlexibleItem<TriS
|
||||
val view = holder.text
|
||||
view.text = filter.name
|
||||
|
||||
fun getIcon() = VectorDrawableCompat.create(view.resources, when (filter.state) {
|
||||
Filter.TriState.STATE_IGNORE -> TR.drawable.ic_check_box_outline_blank_24dp
|
||||
Filter.TriState.STATE_INCLUDE -> TR.drawable.ic_check_box_24dp
|
||||
Filter.TriState.STATE_EXCLUDE -> TR.drawable.ic_check_box_x_24dp
|
||||
else -> throw Exception("Unknown state")
|
||||
}, null)?.apply {
|
||||
val color = if (filter.state == Filter.TriState.STATE_INCLUDE)
|
||||
fun getIcon() = VectorDrawableCompat.create(
|
||||
view.resources,
|
||||
when (filter.state) {
|
||||
Filter.TriState.STATE_IGNORE -> TR.drawable.ic_check_box_outline_blank_24dp
|
||||
Filter.TriState.STATE_INCLUDE -> TR.drawable.ic_check_box_24dp
|
||||
Filter.TriState.STATE_EXCLUDE -> TR.drawable.ic_check_box_x_24dp
|
||||
else -> throw Exception("Unknown state")
|
||||
},
|
||||
null
|
||||
)?.apply {
|
||||
val color = if (filter.state == Filter.TriState.STATE_INCLUDE) {
|
||||
view.context.getResourceColor(R.attr.colorAccent)
|
||||
else
|
||||
} else {
|
||||
ContextCompat.getColor(view.context, R.color.material_on_background_disabled)
|
||||
}
|
||||
|
||||
setTint(color)
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
@@ -12,7 +12,7 @@ import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
* @param controller instance of [GlobalSearchController].
|
||||
*/
|
||||
class GlobalSearchAdapter(val controller: GlobalSearchController) :
|
||||
FlexibleAdapter<GlobalSearchItem>(null, controller, true) {
|
||||
FlexibleAdapter<GlobalSearchItem>(null, controller, true) {
|
||||
|
||||
/**
|
||||
* Bundle where the view state of the holders is saved.
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
@@ -9,7 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
* @param controller instance of [GlobalSearchController].
|
||||
*/
|
||||
class GlobalSearchCardAdapter(controller: GlobalSearchController) :
|
||||
FlexibleAdapter<GlobalSearchCardItem>(null, controller, true) {
|
||||
FlexibleAdapter<GlobalSearchCardItem>(null, controller, true) {
|
||||
|
||||
/**
|
||||
* Listen for browse item clicks.
|
||||
+7
-7
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.view.View
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
@@ -43,12 +43,12 @@ class GlobalSearchCardHolder(view: View, adapter: GlobalSearchCardAdapter) :
|
||||
GlideApp.with(itemView.context).clear(itemImage)
|
||||
if (!manga.thumbnail_url.isNullOrEmpty()) {
|
||||
GlideApp.with(itemView.context)
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.skipMemoryCache(true)
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(StateImageViewTarget(itemImage, progress))
|
||||
.load(manga.toMangaThumbnail())
|
||||
.diskCacheStrategy(DiskCacheStrategy.DATA)
|
||||
.centerCrop()
|
||||
.skipMemoryCache(true)
|
||||
.placeholder(android.R.color.transparent)
|
||||
.into(StateImageViewTarget(itemImage, progress))
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
@@ -31,7 +31,7 @@ open class GlobalSearchController(
|
||||
protected val initialQuery: String? = null,
|
||||
protected val extensionFilter: String? = null
|
||||
) : NucleusController<GlobalSearchControllerBinding, GlobalSearchPresenter>(),
|
||||
GlobalSearchCardAdapter.OnMangaClickListener {
|
||||
GlobalSearchCardAdapter.OnMangaClickListener {
|
||||
|
||||
/**
|
||||
* Adapter containing search results grouped by lang.
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@@ -18,7 +18,7 @@ import kotlinx.android.synthetic.main.global_search_controller_card.title
|
||||
* @param adapter instance of [GlobalSearchAdapter]
|
||||
*/
|
||||
class GlobalSearchHolder(view: View, val adapter: GlobalSearchAdapter) :
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
* Adapter containing manga from search results.
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
+61
-50
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.tachiyomi.ui.source.global_search
|
||||
package eu.kanade.tachiyomi.ui.source.globalsearch
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
@@ -73,11 +73,13 @@ open class GlobalSearchPresenter(
|
||||
super.onCreate(savedState)
|
||||
|
||||
extensionFilter = savedState?.getString(GlobalSearchPresenter::extensionFilter.name)
|
||||
?: initialExtensionFilter
|
||||
?: initialExtensionFilter
|
||||
|
||||
// Perform a search with previous or initial state
|
||||
search(savedState?.getString(BrowseSourcePresenter::query.name)
|
||||
?: initialQuery.orEmpty())
|
||||
search(
|
||||
savedState?.getString(BrowseSourcePresenter::query.name)
|
||||
?: initialQuery.orEmpty()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
@@ -104,9 +106,9 @@ open class GlobalSearchPresenter(
|
||||
val pinnedCatalogues = preferences.pinnedCatalogues().get()
|
||||
|
||||
return sourceManager.getCatalogueSources()
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedWith(compareBy({ it.id.toString() !in pinnedCatalogues }, { "(${it.lang}) ${it.name}" }))
|
||||
.filter { it.lang in languages }
|
||||
.filterNot { it.id.toString() in hiddenCatalogues }
|
||||
.sortedWith(compareBy({ it.id.toString() !in pinnedCatalogues }, { "(${it.lang}) ${it.name}" }))
|
||||
}
|
||||
|
||||
private fun getSourcesToQuery(): List<CatalogueSource> {
|
||||
@@ -117,10 +119,10 @@ open class GlobalSearchPresenter(
|
||||
}
|
||||
|
||||
val filterSources = extensionManager.installedExtensions
|
||||
.filter { it.pkgName == filter }
|
||||
.flatMap { it.sources }
|
||||
.filter { it in enabledSources }
|
||||
.filterIsInstance<CatalogueSource>()
|
||||
.filter { it.pkgName == filter }
|
||||
.flatMap { it.sources }
|
||||
.filter { it in enabledSources }
|
||||
.filterIsInstance<CatalogueSource>()
|
||||
|
||||
if (filterSources.isEmpty()) {
|
||||
return enabledSources
|
||||
@@ -157,29 +159,35 @@ open class GlobalSearchPresenter(
|
||||
|
||||
fetchSourcesSubscription?.unsubscribe()
|
||||
fetchSourcesSubscription = Observable.from(sources)
|
||||
.flatMap({ source ->
|
||||
.flatMap(
|
||||
{ source ->
|
||||
Observable.defer { source.fetchSearchManga(1, query, FilterList()) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions
|
||||
.map { it.mangas.take(10) } // Get at most 10 manga from search result.
|
||||
.map { list -> list.map { networkToLocalManga(it, source.id) } } // Convert to local manga.
|
||||
.doOnNext { fetchImage(it, source) } // Load manga covers.
|
||||
.map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it) }) }
|
||||
}, 5)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// Update matching source with the obtained results
|
||||
.map { result ->
|
||||
items.map { item -> if (item.source == result.source) result else item }
|
||||
}
|
||||
// Update current state
|
||||
.doOnNext { items = it }
|
||||
// Deliver initial state
|
||||
.startWith(initialItems)
|
||||
.subscribeLatestCache({ view, manga ->
|
||||
.subscribeOn(Schedulers.io())
|
||||
.onErrorReturn { MangasPage(emptyList(), false) } // Ignore timeouts or other exceptions
|
||||
.map { it.mangas.take(10) } // Get at most 10 manga from search result.
|
||||
.map { list -> list.map { networkToLocalManga(it, source.id) } } // Convert to local manga.
|
||||
.doOnNext { fetchImage(it, source) } // Load manga covers.
|
||||
.map { list -> createCatalogueSearchItem(source, list.map { GlobalSearchCardItem(it) }) }
|
||||
},
|
||||
5
|
||||
)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// Update matching source with the obtained results
|
||||
.map { result ->
|
||||
items.map { item -> if (item.source == result.source) result else item }
|
||||
}
|
||||
// Update current state
|
||||
.doOnNext { items = it }
|
||||
// Deliver initial state
|
||||
.startWith(initialItems)
|
||||
.subscribeLatestCache(
|
||||
{ view, manga ->
|
||||
view.setItems(manga)
|
||||
}, { _, error ->
|
||||
},
|
||||
{ _, error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -197,21 +205,24 @@ open class GlobalSearchPresenter(
|
||||
private fun initializeFetchImageSubscription() {
|
||||
fetchImageSubscription?.unsubscribe()
|
||||
fetchImageSubscription = fetchImageSubject.observeOn(Schedulers.io())
|
||||
.flatMap { pair ->
|
||||
val source = pair.second
|
||||
Observable.from(pair.first).filter { it.thumbnail_url == null && !it.initialized }
|
||||
.map { Pair(it, source) }
|
||||
.concatMap { getMangaDetailsObservable(it.first, it.second) }
|
||||
.map { Pair(source as CatalogueSource, it) }
|
||||
}
|
||||
.onBackpressureBuffer()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ (source, manga) ->
|
||||
.flatMap { pair ->
|
||||
val source = pair.second
|
||||
Observable.from(pair.first).filter { it.thumbnail_url == null && !it.initialized }
|
||||
.map { Pair(it, source) }
|
||||
.concatMap { getMangaDetailsObservable(it.first, it.second) }
|
||||
.map { Pair(source as CatalogueSource, it) }
|
||||
}
|
||||
.onBackpressureBuffer()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ (source, manga) ->
|
||||
@Suppress("DEPRECATION")
|
||||
view?.onMangaInitialized(source, manga)
|
||||
}, { error ->
|
||||
},
|
||||
{ error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -222,13 +233,13 @@ open class GlobalSearchPresenter(
|
||||
*/
|
||||
private fun getMangaDetailsObservable(manga: Manga, source: Source): Observable<Manga> {
|
||||
return source.fetchMangaDetails(manga)
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just(manga)
|
||||
}
|
||||
.onErrorResumeNext { Observable.just(manga) }
|
||||
.flatMap { networkManga ->
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
Observable.just(manga)
|
||||
}
|
||||
.onErrorResumeNext { Observable.just(manga) }
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12,9 +12,11 @@ import eu.kanade.tachiyomi.ui.source.browse.BrowseSourcePresenter
|
||||
*/
|
||||
class LatestUpdatesController(bundle: Bundle) : BrowseSourceController(bundle) {
|
||||
|
||||
constructor(source: CatalogueSource) : this(Bundle().apply {
|
||||
putLong(SOURCE_ID_KEY, source.id)
|
||||
})
|
||||
constructor(source: CatalogueSource) : this(
|
||||
Bundle().apply {
|
||||
putLong(SOURCE_ID_KEY, source.id)
|
||||
}
|
||||
)
|
||||
|
||||
override fun createPresenter(): BrowseSourcePresenter {
|
||||
return LatestUpdatesPresenter(args.getLong(SOURCE_ID_KEY))
|
||||
|
||||
@@ -14,8 +14,8 @@ class LatestUpdatesPager(val source: CatalogueSource) : Pager() {
|
||||
|
||||
override fun requestNext(): Observable<MangasPage> {
|
||||
return source.fetchLatestUpdates(currentPage)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { onPageReceived(it) }
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnNext { onPageReceived(it) }
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user