Download count shouldn't be stored as a database field
This commit is contained in:
parent
60ac27e401
commit
f88c86c799
@ -6,6 +6,4 @@ class LibraryManga : MangaImpl() {
|
||||
|
||||
var category: Int = 0
|
||||
|
||||
var downloadTotal: Int = 0
|
||||
|
||||
}
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
}
|
||||
|
@ -16,6 +16,8 @@ import kotlinx.android.synthetic.main.catalogue_grid_item.view.*
|
||||
|
||||
class LibraryItem(val manga: LibraryManga) : AbstractFlexibleItem<LibraryHolder>(), IFilterable {
|
||||
|
||||
var downloadCount = -1
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.catalogue_grid_item
|
||||
}
|
||||
@ -43,7 +45,7 @@ class LibraryItem(val manga: LibraryManga) : AbstractFlexibleItem<LibraryHolder>
|
||||
position: Int,
|
||||
payloads: List<Any?>?) {
|
||||
|
||||
holder.onSetValues(manga)
|
||||
holder.onSetValues(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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()
|
||||
|
@ -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<Category>, val mangaMap: LibraryMap)
|
||||
|
||||
/**
|
||||
* Typealias for the library manga, using the category as keys, and list of manga as values.
|
||||
*/
|
||||
private typealias LibraryMap = Map<Int, List<LibraryItem>>
|
||||
|
||||
/**
|
||||
* 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<Int, List<LibraryManga>>): Map<Int, List<LibraryManga>> {
|
||||
private fun applyFilters(map: LibraryMap): LibraryMap {
|
||||
// Cached list of downloaded manga directories given a source id.
|
||||
val mangaDirsForSource = mutableMapOf<Long, Map<String?, UniFile>>()
|
||||
|
||||
@ -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<Int, List<LibraryManga>>): Map<Int, List<LibraryManga>> {
|
||||
// Cached list of downloaded manga directories given a source id.
|
||||
if (preferences.downloadBadge().getOrDefault()) {
|
||||
val mangaDirsForSource = mutableMapOf<Long, Map<String?, UniFile>>()
|
||||
|
||||
// Cached list of downloaded chapter directories for a manga.
|
||||
val chapterDirectories = mutableMapOf<Long, Int>()
|
||||
|
||||
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<Long, Map<String?, UniFile>>, chapterDirectories: MutableMap<Long, Int>): 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<Long, Map<String?, UniFile>>()
|
||||
|
||||
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<Long, Int>()
|
||||
|
||||
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<Int, List<LibraryManga>>): Map<Int, List<LibraryManga>> {
|
||||
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<Pair<List<Category>, Map<Int, List<LibraryManga>>>> {
|
||||
private fun getLibraryObservable(): Observable<Library> {
|
||||
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<Map<Int, List<LibraryManga>>> {
|
||||
private fun getLibraryMangasObservable(): Observable<LibraryMap> {
|
||||
return db.getLibraryMangas().asRxObservable()
|
||||
.map { list -> list.groupBy { it.category } }
|
||||
.map { list -> list.map(::LibraryItem).groupBy { it.manga.category } }
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user