From 3d2a98451b73682d3c5a21e775a221508f803132 Mon Sep 17 00:00:00 2001 From: len Date: Sun, 4 Dec 2016 23:48:29 +0100 Subject: [PATCH] Avoid going to db when a library filter is changed --- .../ui/library/LibraryCategoryAdapter.kt | 10 +- .../tachiyomi/ui/library/LibraryFragment.kt | 8 +- .../tachiyomi/ui/library/LibraryPresenter.kt | 133 +++++++++--------- 3 files changed, 74 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index 1a67ee3a31..c5bc50303e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -23,7 +23,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : /** * The list of manga in this category. */ - private var mangas: List? = null + private var mangas: List = emptyList() init { setHasStableIds(true) @@ -37,7 +37,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : fun setItems(list: List) { mItems = list - // A copy of manga that it's always unfiltered + // A copy of manga always unfiltered. mangas = ArrayList(list) updateDataSet(null) } @@ -58,10 +58,8 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) : * @param param the filter. Not used. */ override fun updateDataSet(param: String?) { - mangas?.let { - filterItems(it) - notifyDataSetChanged() - } + filterItems(mangas) + notifyDataSetChanged() } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt index b873f5f9b8..146fe47e86 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt @@ -153,7 +153,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback if (savedState != null) { activeCategory = savedState.getInt(CATEGORY_KEY) query = savedState.getString(QUERY_KEY) - presenter.searchSubject.onNext(query) + presenter.searchSubject.call(query) if (presenter.selectedMangas.isNotEmpty()) { createActionModeIfNeeded() } @@ -301,7 +301,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback * Applies filter change */ private fun onFilterOrSortChanged() { - presenter.resubscribeLibrary() + presenter.requestLibraryUpdate() activity.supportInvalidateOptionsMenu() } @@ -346,7 +346,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback // Notify the subject the query has changed. if (isResumed) { - presenter.searchSubject.onNext(query) + presenter.searchSubject.call(query) } } @@ -374,7 +374,7 @@ class LibraryFragment : BaseRxFragment(), ActionMode.Callback view_pager.post { if (isAdded) tabs.setScrollPosition(view_pager.currentItem, 0f, true) } // Send the manga map to child fragments after the adapter is updated. - presenter.libraryMangaSubject.onNext(LibraryMangaEvent(mangaMap)) + presenter.libraryMangaSubject.call(LibraryMangaEvent(mangaMap)) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 7b7e390820..f1481e63f7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -2,6 +2,8 @@ package eu.kanade.tachiyomi.ui.library import android.os.Bundle import android.util.Pair +import com.jakewharton.rxrelay.BehaviorRelay +import com.jakewharton.rxrelay.PublishRelay import eu.kanade.tachiyomi.Constants import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper @@ -13,11 +15,11 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.source.SourceManager import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter +import eu.kanade.tachiyomi.util.isNullOrUnsubscribed import rx.Observable +import rx.Subscription import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers -import rx.subjects.BehaviorSubject -import rx.subjects.PublishSubject import uy.kohesive.injekt.injectLazy import java.io.IOException import java.io.InputStream @@ -28,6 +30,31 @@ import java.util.* */ class LibraryPresenter : BasePresenter() { + /** + * Database. + */ + private val db: DatabaseHelper by injectLazy() + + /** + * Preferences. + */ + private val preferences: PreferencesHelper by injectLazy() + + /** + * Cover cache. + */ + private val coverCache: CoverCache by injectLazy() + + /** + * Source manager. + */ + private val sourceManager: SourceManager by injectLazy() + + /** + * Download manager. + */ + private val downloadManager: DownloadManager by injectLazy() + /** * Categories of the library. */ @@ -41,61 +68,52 @@ class LibraryPresenter : BasePresenter() { /** * Search query of the library. */ - val searchSubject: BehaviorSubject = BehaviorSubject.create() + val searchSubject: BehaviorRelay = BehaviorRelay.create() /** * Subject to notify the library's viewpager for updates. */ - val libraryMangaSubject: BehaviorSubject = BehaviorSubject.create() + val libraryMangaSubject: BehaviorRelay = BehaviorRelay.create() /** * Subject to notify the UI of selection updates. */ - val selectionSubject: PublishSubject = PublishSubject.create() + val selectionSubject: PublishRelay = PublishRelay.create() /** - * Database. + * Relay used to apply the UI filters to the last emission of the library. */ - val db: DatabaseHelper by injectLazy() + private val updateTriggerRelay = BehaviorRelay.create(Unit) /** - * Preferences. + * Library subscription. */ - val preferences: PreferencesHelper by injectLazy() - - /** - * Cover cache. - */ - val coverCache: CoverCache by injectLazy() - - /** - * Source manager. - */ - val sourceManager: SourceManager by injectLazy() - - /** - * Download manager. - */ - val downloadManager: DownloadManager by injectLazy() - - companion object { - /** - * Id of the restartable that listens for library updates. - */ - const val GET_LIBRARY = 1 - } + private var librarySubscription: Subscription? = null override fun onCreate(savedState: Bundle?) { super.onCreate(savedState) + subscribeLibrary() + } - restartableLatestCache(GET_LIBRARY, - { getLibraryObservable() }, - { view, pair -> view.onNextLibraryUpdate(pair.first, pair.second) }) - - if (savedState == null) { - start(GET_LIBRARY) + /** + * Subscribes to library if needed. + */ + fun subscribeLibrary() { + if (librarySubscription.isNullOrUnsubscribed()) { + librarySubscription = Observable.combineLatest(getLibraryObservable(), + updateTriggerRelay.observeOn(Schedulers.io()), + { library, updateTrigger -> library }) + .map { Pair(it.first, applyFilters(it.second)) } + .observeOn(AndroidSchedulers.mainThread()) + .subscribeLatestCache({ view, pair -> view.onNextLibraryUpdate(pair.first, pair.second) }) } + } + private fun applyFilters(map: Map>): Map> { + return map.mapValues { entry -> entry.value + .filter { filterManga(it) } + .sortedWith(Comparator { m1, m2 -> sortManga(m1, m2) }) + } } /** @@ -103,7 +121,7 @@ class LibraryPresenter : BasePresenter() { * * @return an observable of the categories and its manga. */ - fun getLibraryObservable(): Observable, Map>>> { + private fun getLibraryObservable(): Observable, Map>>> { return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(), { dbCategories, libraryManga -> val categories = if (libraryManga.containsKey(0)) @@ -114,7 +132,6 @@ class LibraryPresenter : BasePresenter() { this.categories = categories Pair(categories, libraryManga) }) - .observeOn(AndroidSchedulers.mainThread()) } /** @@ -122,7 +139,7 @@ class LibraryPresenter : BasePresenter() { * * @return an observable of the categories. */ - fun getCategoriesObservable(): Observable> { + private fun getCategoriesObservable(): Observable> { return db.getCategories().asRxObservable() } @@ -132,34 +149,16 @@ class LibraryPresenter : BasePresenter() { * @return an observable containing a map with the category id as key and a list of manga as the * value. */ - fun getLibraryMangasObservable(): Observable>> { + private fun getLibraryMangasObservable(): Observable>> { return db.getLibraryMangas().asRxObservable() - .flatMap { - Observable.from(it) - // Filter library by options - .filter { filterManga(it) } - .toSortedList { manga1, manga2 -> sortManga(manga1, manga2) } - .flatMap { Observable.from(it) } - .groupBy { it.category } - .flatMap { group -> group.toList().map { Pair(group.key, it) } } - .toMap({ it.first }, { it.second }) - } + .map { list -> list.groupBy { it.category } } } /** - * Resubscribes to library if needed. + * Requests the library to be filtered. */ - fun subscribeLibrary() { - if (isUnsubscribed(GET_LIBRARY)) { - start(GET_LIBRARY) - } - } - - /** - * Resubscribes to library. - */ - fun resubscribeLibrary() { - start(GET_LIBRARY) + fun requestLibraryUpdate() { + updateTriggerRelay.call(Unit) } /** @@ -238,7 +237,7 @@ class LibraryPresenter : BasePresenter() { */ fun onOpenManga() { // Avoid further db updates for the library when it's not needed - stop(GET_LIBRARY) + librarySubscription?.let { remove(it) } } /** @@ -250,10 +249,10 @@ class LibraryPresenter : BasePresenter() { fun setSelection(manga: Manga, selected: Boolean) { if (selected) { selectedMangas.add(manga) - selectionSubject.onNext(LibrarySelectionEvent.Selected(manga)) + selectionSubject.call(LibrarySelectionEvent.Selected(manga)) } else { selectedMangas.remove(manga) - selectionSubject.onNext(LibrarySelectionEvent.Unselected(manga)) + selectionSubject.call(LibrarySelectionEvent.Unselected(manga)) } } @@ -262,7 +261,7 @@ class LibraryPresenter : BasePresenter() { */ fun clearSelections() { selectedMangas.clear() - selectionSubject.onNext(LibrarySelectionEvent.Cleared()) + selectionSubject.call(LibrarySelectionEvent.Cleared()) } /**