Pārlūkot izejas kodu

Avoid going to db when a library filter is changed

len 8 gadi atpakaļ
vecāks
revīzija
3d2a98451b

+ 4 - 6
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<Manga>? = null
+    private var mangas: List<Manga> = emptyList()
 
     init {
         setHasStableIds(true)
@@ -37,7 +37,7 @@ class LibraryCategoryAdapter(val fragment: LibraryCategoryView) :
     fun setItems(list: List<Manga>) {
         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()
     }
 
     /**

+ 4 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt

@@ -153,7 +153,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), 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<LibraryPresenter>(), ActionMode.Callback
      * Applies filter change
      */
     private fun onFilterOrSortChanged() {
-        presenter.resubscribeLibrary()
+        presenter.requestLibraryUpdate()
         activity.supportInvalidateOptionsMenu()
     }
 
@@ -346,7 +346,7 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), 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<LibraryPresenter>(), 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))
     }
 
     /**

+ 63 - 64
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
@@ -29,73 +31,89 @@ import java.util.*
 class LibraryPresenter : BasePresenter<LibraryFragment>() {
 
     /**
-     * Categories of the library.
+     * Database.
      */
-    var categories: List<Category> = emptyList()
+    private val db: DatabaseHelper by injectLazy()
 
     /**
-     * Currently selected manga.
+     * Preferences.
      */
-    val selectedMangas = mutableListOf<Manga>()
+    private val preferences: PreferencesHelper by injectLazy()
 
     /**
-     * Search query of the library.
+     * Cover cache.
      */
-    val searchSubject: BehaviorSubject<String> = BehaviorSubject.create()
+    private val coverCache: CoverCache by injectLazy()
 
     /**
-     * Subject to notify the library's viewpager for updates.
+     * Source manager.
      */
-    val libraryMangaSubject: BehaviorSubject<LibraryMangaEvent> = BehaviorSubject.create()
+    private val sourceManager: SourceManager by injectLazy()
 
     /**
-     * Subject to notify the UI of selection updates.
+     * Download manager.
      */
-    val selectionSubject: PublishSubject<LibrarySelectionEvent> = PublishSubject.create()
+    private val downloadManager: DownloadManager by injectLazy()
 
     /**
-     * Database.
+     * Categories of the library.
      */
-    val db: DatabaseHelper by injectLazy()
+    var categories: List<Category> = emptyList()
 
     /**
-     * Preferences.
+     * Currently selected manga.
      */
-    val preferences: PreferencesHelper by injectLazy()
+    val selectedMangas = mutableListOf<Manga>()
 
     /**
-     * Cover cache.
+     * Search query of the library.
      */
-    val coverCache: CoverCache by injectLazy()
+    val searchSubject: BehaviorRelay<String> = BehaviorRelay.create()
 
     /**
-     * Source manager.
+     * Subject to notify the library's viewpager for updates.
      */
-    val sourceManager: SourceManager by injectLazy()
+    val libraryMangaSubject: BehaviorRelay<LibraryMangaEvent> = BehaviorRelay.create()
 
     /**
-     * Download manager.
+     * Subject to notify the UI of selection updates.
      */
-    val downloadManager: DownloadManager by injectLazy()
+    val selectionSubject: PublishRelay<LibrarySelectionEvent> = PublishRelay.create()
 
-    companion object {
-        /**
-         * Id of the restartable that listens for library updates.
-         */
-        const val GET_LIBRARY = 1
-    }
+    /**
+     * Relay used to apply the UI filters to the last emission of the library.
+     */
+    private val updateTriggerRelay = BehaviorRelay.create(Unit)
+
+    /**
+     * Library subscription.
+     */
+    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<Int, List<Manga>>): Map<Int, List<Manga>> {
+        return map.mapValues { entry -> entry.value
+                .filter { filterManga(it) }
+                .sortedWith(Comparator<Manga> { m1, m2 -> sortManga(m1, m2) })
+        }
     }
 
     /**
@@ -103,7 +121,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
      *
      * @return an observable of the categories and its manga.
      */
-    fun getLibraryObservable(): Observable<Pair<List<Category>, Map<Int, List<Manga>>>> {
+    private fun getLibraryObservable(): Observable<Pair<List<Category>, Map<Int, List<Manga>>>> {
         return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(),
                 { dbCategories, libraryManga ->
                     val categories = if (libraryManga.containsKey(0))
@@ -114,7 +132,6 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
                     this.categories = categories
                     Pair(categories, libraryManga)
                 })
-                .observeOn(AndroidSchedulers.mainThread())
     }
 
     /**
@@ -122,7 +139,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
      *
      * @return an observable of the categories.
      */
-    fun getCategoriesObservable(): Observable<List<Category>> {
+    private fun getCategoriesObservable(): Observable<List<Category>> {
         return db.getCategories().asRxObservable()
     }
 
@@ -132,34 +149,16 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
      * @return an observable containing a map with the category id as key and a list of manga as the
      * value.
      */
-    fun getLibraryMangasObservable(): Observable<Map<Int, List<Manga>>> {
+    private fun getLibraryMangasObservable(): Observable<Map<Int, List<Manga>>> {
         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 })
-                }
-    }
-
-    /**
-     * Resubscribes to library if needed.
-     */
-    fun subscribeLibrary() {
-        if (isUnsubscribed(GET_LIBRARY)) {
-            start(GET_LIBRARY)
-        }
+                .map { list -> list.groupBy { it.category } }
     }
 
     /**
-     * Resubscribes to library.
+     * Requests the library to be filtered.
      */
-    fun resubscribeLibrary() {
-        start(GET_LIBRARY)
+    fun requestLibraryUpdate() {
+        updateTriggerRelay.call(Unit)
     }
 
     /**
@@ -238,7 +237,7 @@ class LibraryPresenter : BasePresenter<LibraryFragment>() {
      */
     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<LibraryFragment>() {
     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<LibraryFragment>() {
      */
     fun clearSelections() {
         selectedMangas.clear()
-        selectionSubject.onNext(LibrarySelectionEvent.Cleared())
+        selectionSubject.call(LibrarySelectionEvent.Cleared())
     }
 
     /**