diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/LibraryManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/LibraryManga.kt index bff35d14e4..b9a7d94286 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/LibraryManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/LibraryManga.kt @@ -6,6 +6,4 @@ class LibraryManga : MangaImpl() { var category: Int = 0 - var downloadTotal: Int = 0 - } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt index f2dc16f851..429e80e986 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGridHolder.kt @@ -3,15 +3,9 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.kanade.tachiyomi.data.database.models.LibraryManga -import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.LocalSource import kotlinx.android.synthetic.main.catalogue_grid_item.view.* -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -27,37 +21,36 @@ class LibraryGridHolder( private val adapter: FlexibleAdapter<*> ) : LibraryHolder(view, adapter) { - private val preferences: PreferencesHelper = Injekt.get() /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this * holder with the given manga. * - * @param manga the manga to bind. + * @param item the manga item to bind. */ - override fun onSetValues(manga: LibraryManga) { + override fun onSetValues(item: LibraryItem) { // Update the title of the manga. - view.title.text = manga.title + view.title.text = item.manga.title // Update the unread count and its visibility. with(view.unread_text) { - visibility = if (manga.unread > 0) View.VISIBLE else View.GONE - text = manga.unread.toString() + visibility = if (item.manga.unread > 0) View.VISIBLE else View.GONE + text = item.manga.unread.toString() } // Update the download count and its visibility. with(view.download_text) { - visibility = if (manga.downloadTotal > 0 && preferences.downloadBadge().getOrDefault()) View.VISIBLE else View.GONE - text = manga.downloadTotal.toString() + visibility = if (item.downloadCount > 0) View.VISIBLE else View.GONE + text = item.downloadCount.toString() } //set local visibility if its local manga - with(view.local_text){ - visibility = if(manga.source == LocalSource.ID) View.VISIBLE else View.GONE + with(view.local_text) { + visibility = if(item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE } // Update the cover. GlideApp.with(view.context).clear(view.thumbnail) GlideApp.with(view.context) - .load(manga) + .load(item.manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() .into(view.thumbnail) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt index 9e9275159c..ff29155c45 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHolder.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.viewholders.FlexibleViewHolder -import eu.kanade.tachiyomi.data.database.models.LibraryManga /** * Generic class used to hold the displayed data of a manga in the library. @@ -21,8 +20,8 @@ abstract class LibraryHolder( * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this * holder with the given manga. * - * @param manga the manga to bind. + * @param item the manga item to bind. */ - abstract fun onSetValues(manga: LibraryManga) + abstract fun onSetValues(item: LibraryItem) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt index 25e34f0a2c..5d7f21f1d0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryItem.kt @@ -16,6 +16,8 @@ import kotlinx.android.synthetic.main.catalogue_grid_item.view.* class LibraryItem(val manga: LibraryManga) : AbstractFlexibleItem(), IFilterable { + var downloadCount = -1 + override fun getLayoutRes(): Int { return R.layout.catalogue_grid_item } @@ -43,7 +45,7 @@ class LibraryItem(val manga: LibraryManga) : AbstractFlexibleItem position: Int, payloads: List?) { - holder.onSetValues(manga) + holder.onSetValues(this) } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt index b36d02f8ba..37a23fe37a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryListHolder.kt @@ -3,14 +3,9 @@ package eu.kanade.tachiyomi.ui.library import android.view.View import com.bumptech.glide.load.engine.DiskCacheStrategy import eu.davidea.flexibleadapter.FlexibleAdapter -import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.glide.GlideApp -import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.source.LocalSource import kotlinx.android.synthetic.main.catalogue_list_item.view.* -import uy.kohesive.injekt.Injekt -import uy.kohesive.injekt.api.get /** * Class used to hold the displayed data of a manga in the library, like the cover or the title. @@ -26,31 +21,30 @@ class LibraryListHolder( private val view: View, private val adapter: FlexibleAdapter<*> ) : LibraryHolder(view, adapter) { - private val preferences: PreferencesHelper = Injekt.get() /** * Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this * holder with the given manga. * - * @param manga the manga to bind. + * @param item the manga item to bind. */ - override fun onSetValues(manga: LibraryManga) { + override fun onSetValues(item: LibraryItem) { // Update the title of the manga. - itemView.title.text = manga.title + itemView.title.text = item.manga.title // Update the unread count and its visibility. with(itemView.unread_text) { - visibility = if (manga.unread > 0) View.VISIBLE else View.GONE - text = manga.unread.toString() + visibility = if (item.manga.unread > 0) View.VISIBLE else View.GONE + text = item.manga.unread.toString() } // Update the download count and its visibility. with(itemView.download_text) { - visibility = if (manga.downloadTotal > 0 && preferences.downloadBadge().getOrDefault()) View.VISIBLE else View.GONE - text = manga.downloadTotal.toString() + visibility = if (item.downloadCount > 0) View.VISIBLE else View.GONE + text = "${item.downloadCount}" } //show local text badge if local manga with(itemView.local_text) { - visibility = if (manga.source == LocalSource.ID) View.VISIBLE else View.GONE + visibility = if (item.manga.source == LocalSource.ID) View.VISIBLE else View.GONE } // Create thumbnail onclick to simulate long click @@ -62,7 +56,7 @@ class LibraryListHolder( // Update the cover. GlideApp.with(itemView.context).clear(itemView.thumbnail) GlideApp.with(itemView.context) - .load(manga) + .load(item.manga) .diskCacheStrategy(DiskCacheStrategy.RESOURCE) .centerCrop() .circleCrop() 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 1a9fcb4187..3a12a3581e 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 @@ -1,13 +1,11 @@ package eu.kanade.tachiyomi.ui.library import android.os.Bundle -import android.util.Pair import com.hippo.unifile.UniFile import com.jakewharton.rxrelay.BehaviorRelay import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category -import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.MangaCategory import eu.kanade.tachiyomi.data.download.DownloadManager @@ -30,6 +28,16 @@ import java.io.IOException import java.io.InputStream import java.util.* +/** + * Class containing library information. + */ +private data class Library(val categories: List, val mangaMap: LibraryMap) + +/** + * Typealias for the library manga, using the category as keys, and list of manga as values. + */ +private typealias LibraryMap = Map> + /** * Presenter of [LibraryController]. */ @@ -80,16 +88,15 @@ class LibraryPresenter( fun subscribeLibrary() { if (librarySubscription.isNullOrUnsubscribed()) { librarySubscription = getLibraryObservable() - .combineLatest(filterTriggerRelay.observeOn(Schedulers.io()), - { lib, _ -> Pair(lib.first, applyFilters(lib.second)) }) .combineLatest(downloadTriggerRelay.observeOn(Schedulers.io()), - { lib, _ -> Pair(lib.first, addDownloadTotal(lib.second)) }) + { lib, _ -> lib.apply { setDownloadCount(mangaMap) } }) + .combineLatest(filterTriggerRelay.observeOn(Schedulers.io()), + { lib, _ -> lib.copy(mangaMap = applyFilters(lib.mangaMap)) }) .combineLatest(sortTriggerRelay.observeOn(Schedulers.io()), - { lib, _ -> Pair(lib.first, applySort(lib.second)) }) - .map { Pair(it.first, it.second.mapValues { it.value.map(::LibraryItem) }) } + { lib, _ -> lib.copy(mangaMap = applySort(lib.mangaMap)) }) .observeOn(AndroidSchedulers.mainThread()) - .subscribeLatestCache({ view, pair -> - view.onNextLibraryUpdate(pair.first, pair.second) + .subscribeLatestCache({ view, (categories, mangaMap) -> + view.onNextLibraryUpdate(categories, mangaMap) }) } } @@ -99,7 +106,7 @@ class LibraryPresenter( * * @param map the map to filter. */ - private fun applyFilters(map: Map>): Map> { + private fun applyFilters(map: LibraryMap): LibraryMap { // Cached list of downloaded manga directories given a source id. val mangaDirsForSource = mutableMapOf>() @@ -112,31 +119,36 @@ class LibraryPresenter( val filterCompleted = preferences.filterCompleted().getOrDefault() - val filterFn: (LibraryManga) -> Boolean = f@ { manga -> + val filterFn: (LibraryItem) -> Boolean = f@ { item -> // Filter out manga without source. - val source = sourceManager.get(manga.source) ?: return@f false + val source = sourceManager.get(item.manga.source) ?: return@f false // Filter when there isn't unread chapters. - if (filterUnread && manga.unread == 0) { + if (filterUnread && item.manga.unread == 0) { return@f false } - if (filterCompleted && manga.status != SManga.COMPLETED) { + if (filterCompleted && item.manga.status != SManga.COMPLETED) { return@f false } // Filter when the download directory doesn't exist or is null. if (filterDownloaded) { + // Don't bother with directory checking if download count has been set. + if (item.downloadCount != -1) { + return@f item.downloadCount > 0 + } + // Get the directories for the source of the manga. val dirsForSource = mangaDirsForSource.getOrPut(source.id) { val sourceDir = downloadManager.findSourceDir(source) sourceDir?.listFiles()?.associateBy { it.name }.orEmpty() } - val mangaDirName = downloadManager.getMangaDirName(manga) + val mangaDirName = downloadManager.getMangaDirName(item.manga) val mangaDir = dirsForSource[mangaDirName] ?: return@f false - val hasDirs = chapterDirectories.getOrPut(manga.id!!) { + val hasDirs = chapterDirectories.getOrPut(item.manga.id!!) { mangaDir.listFiles()?.isNotEmpty() ?: false } if (!hasDirs) { @@ -150,45 +162,48 @@ class LibraryPresenter( } /** - * Adds Downloaded chapter count to manga + * Sets downloaded chapter count to each manga. * - * @param map the map to filter. + * @param map the map of manga. */ - private fun addDownloadTotal(map: Map>): Map> { - // Cached list of downloaded manga directories given a source id. - if (preferences.downloadBadge().getOrDefault()) { - val mangaDirsForSource = mutableMapOf>() - - // Cached list of downloaded chapter directories for a manga. - val chapterDirectories = mutableMapOf() - - for ((key, mangaList) in map) { - for (manga in mangaList) { - manga.downloadTotal = getDownloadedCountFromDirectory(manga, mangaDirsForSource, chapterDirectories) + private fun setDownloadCount(map: LibraryMap) { + if (!preferences.downloadBadge().getOrDefault()) { + // Unset download count if the preference is not enabled. + for ((_, itemList) in map) { + for (item in itemList) { + item.downloadCount = -1 } } + return } - return map; - } - //Get count of downloaded chapters for a manga - fun getDownloadedCountFromDirectory(manga: Manga, mangaDirsForSource: MutableMap>, chapterDirectories: MutableMap): Int { - val source = sourceManager.get(manga.source) ?: return 0; - // Get the directories for the source of the manga. - val dirsForSource = mangaDirsForSource.getOrPut(source.id) { - val sourceDir = downloadManager.findSourceDir(source) - sourceDir?.listFiles()?.associateBy { it.name }.orEmpty() - } - val mangaDirName = downloadManager.getMangaDirName(manga) - val mangaDir = dirsForSource[mangaDirName] ?: return 0 + // Cached list of downloaded manga directories given a source id. + val mangaDirsForSource = mutableMapOf>() - chapterDirectories.getOrPut(manga.id!!) { - if (mangaDir.listFiles()?.isNotEmpty() ?: false) { - return mangaDir.listFiles()!!.size + // Cached list of downloaded chapter directories for a manga. + val chapterDirectories = mutableMapOf() + + val downloadCountFn: (LibraryItem) -> Int = f@ { item -> + val source = sourceManager.get(item.manga.source) ?: return@f 0 + + // Get the directories for the source of the manga. + val dirsForSource = mangaDirsForSource.getOrPut(source.id) { + val sourceDir = downloadManager.findSourceDir(source) + sourceDir?.listFiles()?.associateBy { it.name }.orEmpty() + } + val mangaDirName = downloadManager.getMangaDirName(item.manga) + val mangaDir = dirsForSource[mangaDirName] ?: return@f 0 + + chapterDirectories.getOrPut(item.manga.id!!) { + mangaDir.listFiles()?.size ?: 0 + } + } + + for ((_, itemList) in map) { + for (item in itemList) { + item.downloadCount = downloadCountFn(item) } - return 0; } - return 0; } /** @@ -196,7 +211,7 @@ class LibraryPresenter( * * @param map the map to sort. */ - private fun applySort(map: Map>): Map> { + private fun applySort(map: LibraryMap): LibraryMap { val sortingMode = preferences.librarySortingMode().getOrDefault() val lastReadManga by lazy { @@ -208,25 +223,25 @@ class LibraryPresenter( db.getTotalChapterManga().executeAsBlocking().associate { it.id!! to counter++ } } - val sortFn: (LibraryManga, LibraryManga) -> Int = { manga1, manga2 -> + val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> when (sortingMode) { - LibrarySort.ALPHA -> manga1.title.compareTo(manga2.title) + LibrarySort.ALPHA -> i1.manga.title.compareTo(i2.manga.title) LibrarySort.LAST_READ -> { // Get index of manga, set equal to list if size unknown. - val manga1LastRead = lastReadManga[manga1.id!!] ?: lastReadManga.size - val manga2LastRead = lastReadManga[manga2.id!!] ?: lastReadManga.size + val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size + val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size manga1LastRead.compareTo(manga2LastRead) } - LibrarySort.LAST_UPDATED -> manga2.last_update.compareTo(manga1.last_update) - LibrarySort.UNREAD -> manga1.unread.compareTo(manga2.unread) + LibrarySort.LAST_UPDATED -> i2.manga.last_update.compareTo(i1.manga.last_update) + LibrarySort.UNREAD -> i1.manga.unread.compareTo(i2.manga.unread) LibrarySort.TOTAL -> { - val manga1TotalChapter = totalChapterManga[manga1.id!!] ?: 0 - val mange2TotalChapter = totalChapterManga[manga2.id!!] ?: 0 + val manga1TotalChapter = totalChapterManga[i1.manga.id!!] ?: 0 + val mange2TotalChapter = totalChapterManga[i2.manga.id!!] ?: 0 manga1TotalChapter.compareTo(mange2TotalChapter) } LibrarySort.SOURCE -> { - val source1Name = sourceManager.get(manga1.source)?.name ?: "" - val source2Name = sourceManager.get(manga2.source)?.name ?: "" + val source1Name = sourceManager.get(i1.manga.source)?.name ?: "" + val source2Name = sourceManager.get(i2.manga.source)?.name ?: "" source1Name.compareTo(source2Name) } else -> throw Exception("Unknown sorting mode") @@ -246,7 +261,7 @@ class LibraryPresenter( * * @return an observable of the categories and its manga. */ - private fun getLibraryObservable(): Observable, Map>>> { + private fun getLibraryObservable(): Observable { return Observable.combineLatest(getCategoriesObservable(), getLibraryMangasObservable(), { dbCategories, libraryManga -> val categories = if (libraryManga.containsKey(0)) @@ -255,7 +270,7 @@ class LibraryPresenter( dbCategories this.categories = categories - Pair(categories, libraryManga) + Library(categories, libraryManga) }) } @@ -274,9 +289,9 @@ class LibraryPresenter( * @return an observable containing a map with the category id as key and a list of manga as the * value. */ - private fun getLibraryMangasObservable(): Observable>> { + private fun getLibraryMangasObservable(): Observable { return db.getLibraryMangas().asRxObservable() - .map { list -> list.groupBy { it.category } } + .map { list -> list.map(::LibraryItem).groupBy { it.manga.category } } } /**