diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt index 3fbecd9112..73ff8d1f6c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt @@ -19,14 +19,15 @@ interface Category : Serializable { var mangaSort: Char? - var isFirst: Boolean? - var isLast: Boolean? + var isAlone: Boolean val nameLower: String get() = name.toLowerCase() var isHidden: Boolean + var isDynamic: Boolean + fun isAscending(): Boolean { return ((mangaSort?.minus('a') ?: 0) % 2) != 1 } @@ -49,7 +50,7 @@ interface Category : Serializable { LAST_READ_ASC, LAST_READ_DSC -> R.string.last_read TOTAL_ASC, TOTAL_DSC -> R.string.total_chapters DATE_ADDED_ASC, DATE_ADDED_DSC -> R.string.date_added - else -> if (id == -1) R.string.category else R.string.drag_and_drop + else -> if (isDynamic) R.string.category else R.string.drag_and_drop } fun catSortingMode(): Int? = when (mangaSort) { @@ -96,12 +97,10 @@ interface Category : Serializable { fun createDefault(context: Context): Category = create(context.getString(R.string.default_value)).apply { id = 0 - isFirst = true } - fun createAll(context: Context, libSort: Int, ascending: Boolean): Category = - create(context.getString(R.string.all)).apply { - id = -1 + fun createCustom(name: String, libSort: Int, ascending: Boolean): Category = + create(name).apply { mangaSort = when (libSort) { LibrarySort.ALPHA -> ALPHA_ASC LibrarySort.LATEST_CHAPTER -> UPDATED_ASC @@ -113,11 +112,15 @@ interface Category : Serializable { else -> DRAG_AND_DROP } if (mangaSort != DRAG_AND_DROP && !ascending) { - mangaSort?.plus(1) + mangaSort = mangaSort?.plus(1) } + isDynamic = true + } + + fun createAll(context: Context, libSort: Int, ascending: Boolean): Category = + createCustom(context.getString(R.string.all), libSort, ascending).apply { + id = -1 order = -1 - isFirst = true - isLast = true } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt index ad5921b977..6b27e0eee8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt @@ -14,12 +14,12 @@ class CategoryImpl : Category { override var mangaSort: Char? = null - override var isFirst: Boolean? = null - - override var isLast: Boolean? = null + override var isAlone: Boolean = false override var isHidden: Boolean = false + override var isDynamic: Boolean = false + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false 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 1297c28b10..d6cdd4a2cc 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 @@ -3,10 +3,15 @@ package eu.kanade.tachiyomi.data.database.models class LibraryManga : MangaImpl() { var unread: Int = 0 + var read: Int = 0 var category: Int = 0 - var hasRead: Boolean = false + val totalChapters + get() = read + unread + + val hasRead + get() = read > 0 companion object { fun createBlank(categoryId: Int): LibraryManga = LibraryManga().apply { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/LibraryMangaGetResolver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/LibraryMangaGetResolver.kt index e70e19ada4..f3113d8681 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/LibraryMangaGetResolver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/resolvers/LibraryMangaGetResolver.kt @@ -18,7 +18,7 @@ class LibraryMangaGetResolver : DefaultGetResolver(), BaseMangaGet mapBaseFromCursor(manga, cursor) manga.unread = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_UNREAD)) manga.category = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_CATEGORY)) - manga.hasRead = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_HAS_READ)) > 0 + manga.read = cursor.getInt(cursor.getColumnIndex(MangaTable.COL_HAS_READ)) return manga } 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 e81bade047..d0e3ed7d77 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 @@ -155,7 +155,7 @@ class LibraryCategoryAdapter(val controller: LibraryController) : else recyclerView.context.getString(R.string.read) } LibrarySort.TOTAL -> { - val total = item.chapterCount + val total = item.manga.totalChapters if (total > 0) recyclerView.resources.getQuantityString( R.plurals.chapters, total, total ) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index bb8fa623ba..f7a3d44d3a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -1109,8 +1109,9 @@ class LibraryController( override fun manageCategory(position: Int) { val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return - if (category.id ?: 0 > -1) + if (!category.isDynamic) { ManageCategoryDialog(this, category).showDialog(router) + } } override fun sortCategory(catId: Int, sortBy: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt index 47b58f3f3a..8a5e83fdf8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt @@ -143,7 +143,7 @@ class LibraryHeaderItem( } val category = item.category - if (category.isFirst == true && category.isLast == true) sectionText.text = "" + if (category.isAlone) sectionText.text = "" else sectionText.text = category.name sortText.text = itemView.context.getString( R.string.sort_by_, itemView.context.getString(category.sortRes()) @@ -165,14 +165,15 @@ class LibraryHeaderItem( when { adapter.mode == SelectableAdapter.Mode.MULTI -> { checkboxImage.visibleIf(!category.isHidden) - expandImage.visibleIf(category.isHidden && !adapter.isSingleCategory) + expandImage.visibleIf(category.isHidden && !adapter.isSingleCategory && !category.isDynamic) updateButton.gone() catProgress.gone() setSelection() } - category.id == -1 -> { + category.isDynamic -> { expandImage.gone() checkboxImage.gone() + catProgress.gone() updateButton.gone() } LibraryUpdateService.categoryInQueue(category.id) -> { @@ -185,7 +186,7 @@ class LibraryHeaderItem( expandImage.visibleIf(!adapter.isSingleCategory) catProgress.gone() checkboxImage.gone() - updateButton.visibleIf(category.id ?: 0 > -1 && !adapter.isSingleCategory) + updateButton.visibleIf(!adapter.isSingleCategory) } } } 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 8f87fcfa2c..c5f3b41c6c 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 @@ -26,9 +26,10 @@ abstract class LibraryHolder( abstract fun onSetValues(item: LibraryItem) fun setUnreadBadge(badge: LibraryBadge, item: LibraryItem) { + val showTotal = item.header.category.sortingMode() == LibrarySort.TOTAL badge.setUnreadDownload( when { - item.chapterCount > -1 -> item.chapterCount + showTotal -> item.manga.totalChapters item.unreadType == 2 -> item.manga.unread item.unreadType == 1 -> if (item.manga.unread > 0) -1 else -2 else -> -2 @@ -38,7 +39,7 @@ abstract class LibraryHolder( item.manga.source == LocalSource.ID -> -2 else -> item.downloadCount }, - item.chapterCount > -1) + showTotal) } fun setReadingButton(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 54489a9e33..3cf00fb545 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 @@ -35,7 +35,6 @@ class LibraryItem( var downloadCount = -1 var unreadType = 2 - var chapterCount = -1 private val uniformSize: Boolean get() = preferences.uniformGrid().getOrDefault() 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 69caea4e8d..cf56ad33d2 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 @@ -54,6 +54,10 @@ class LibraryPresenter( var categories: List = emptyList() private set + var hashCategories: HashMap = hashMapOf() + + var removeArticles: Boolean = preferences.removeArticles().getOrDefault() + /** All categories of the library, in case they are hidden because of hide categories is on */ var allCategories: List = emptyList() private set @@ -67,8 +71,6 @@ class LibraryPresenter( val showAllCategories get() = preferences.showAllCategories().get() - private var totalChapters: Map? = null - /** Save the current list to speed up loading later */ fun onDestroy() { lastLibraryItems = libraryItems @@ -86,7 +88,6 @@ class LibraryPresenter( /** Get favorited manga for library and sort and filter it */ fun getLibrary() { scope.launch { - totalChapters = null val library = withContext(Dispatchers.IO) { getLibraryFromDB() } library.apply { setDownloadCount(library) @@ -98,9 +99,6 @@ class LibraryPresenter( mangaMap = applySort(mangaMap) val freshStart = libraryItems.isEmpty() sectionLibrary(mangaMap, freshStart) - withContext(Dispatchers.IO) { - setTotalChapters() - } } } @@ -313,74 +311,81 @@ class LibraryPresenter( * @param itemList the map to sort. */ private fun applySort(itemList: List): List { - val sortingMode = preferences.librarySortingMode().getOrDefault() - val lastReadManga by lazy { var counter = 0 db.getLastReadManga().executeAsBlocking().associate { it.id!! to counter++ } } - val ascending = preferences.librarySortingAscending().getOrDefault() - val useDnD = !preferences.hideCategories().getOrDefault() - val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> - if (!(sortingMode == LibrarySort.DRAG_AND_DROP || useDnD)) { - i1.chapterCount = -1 - i2.chapterCount = -1 - } - val compare = when { - sortingMode == LibrarySort.DRAG_AND_DROP || useDnD -> - sortCategory(i1, i2, lastReadManga) - sortingMode == LibrarySort.ALPHA -> sortAlphabetical(i1, i2) - sortingMode == LibrarySort.LAST_READ -> { - // Get index of manga, set equal to list if size unknown. - val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size - val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size - manga1LastRead.compareTo(manga2LastRead) + if (i1.header.category.id == i2.header.category.id) { + val category = i1.header.category + if (category.mangaOrder.isNullOrEmpty() && category.mangaSort == null) { + category.changeSortTo(preferences.librarySortingMode().getOrDefault()) + if (category.id == 0) preferences.defaultMangaOrder() + .set(category.mangaSort.toString()) + else if (!category.isDynamic) db.insertCategory(category).executeAsBlocking() } - sortingMode == LibrarySort.LATEST_CHAPTER -> i2.manga.last_update.compareTo(i1 - .manga.last_update) - sortingMode == LibrarySort.UNREAD -> - when { - i1.manga.unread == i2.manga.unread -> 0 - i1.manga.unread == 0 -> if (ascending) 1 else -1 - i2.manga.unread == 0 -> if (ascending) -1 else 1 - else -> i1.manga.unread.compareTo(i2.manga.unread) + val compare = when { + category.mangaSort != null -> { + var sort = when (category.sortingMode()) { + LibrarySort.ALPHA -> sortAlphabetical(i1, i2) + LibrarySort.LATEST_CHAPTER -> i2.manga.last_update.compareTo(i1.manga.last_update) + LibrarySort.UNREAD -> when { + i1.manga.unread == i2.manga.unread -> 0 + i1.manga.unread == 0 -> if (category.isAscending()) 1 else -1 + i2.manga.unread == 0 -> if (category.isAscending()) -1 else 1 + else -> i1.manga.unread.compareTo(i2.manga.unread) + } + LibrarySort.LAST_READ -> { + val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size + val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size + manga1LastRead.compareTo(manga2LastRead) + } + LibrarySort.TOTAL -> { + i1.manga.totalChapters.compareTo(i2.manga.totalChapters) + } + LibrarySort.DATE_ADDED -> i2.manga.date_added.compareTo(i1.manga.date_added) + else -> { + if (LibrarySort.DRAG_AND_DROP == category.sortingMode() && category.isDynamic) { + val category1 = + allCategories.find { i1.manga.category == it.id }?.order + ?: 0 + val category2 = + allCategories.find { i2.manga.category == it.id }?.order + ?: 0 + category1.compareTo(category2) + } else { + sortAlphabetical(i1, i2) + } + } + } + if (!category.isAscending()) sort *= -1 + sort } - sortingMode == LibrarySort.TOTAL -> { - setTotalChapters() - val manga1TotalChapter = totalChapters!![i1.manga.id!!] ?: 0 - val mange2TotalChapter = totalChapters!![i2.manga.id!!] ?: 0 - i1.chapterCount = totalChapters!![i1.manga.id!!] ?: 0 - i2.chapterCount = totalChapters!![i2.manga.id!!] ?: 0 - manga1TotalChapter.compareTo(mange2TotalChapter) + category.mangaOrder.isNotEmpty() -> { + val order = category.mangaOrder + val index1 = order.indexOf(i1.manga.id!!) + val index2 = order.indexOf(i2.manga.id!!) + when { + index1 == index2 -> 0 + index1 == -1 -> -1 + index2 == -1 -> 1 + else -> index1.compareTo(index2) + } + } + else -> 0 } - sortingMode == LibrarySort.DATE_ADDED -> { - i2.manga.date_added.compareTo(i1.manga.date_added) - } - else -> 0 + if (compare == 0) { + sortAlphabetical(i1, i2) + } else compare + } else { + val category = i1.header.category.order + val category2 = i2.header.category.order + category.compareTo(category2) } - if (!(sortingMode == LibrarySort.DRAG_AND_DROP || useDnD) && compare == 0) { - if (ascending) sortAlphabetical(i1, i2) - else sortAlphabetical(i2, i1) - } else compare } - val comparator = if (ascending || useDnD) - Comparator(sortFn) - else - Collections.reverseOrder(sortFn) - - return itemList.sortedWith(comparator) - } - - /** Set the total chapters for the manga in the library */ - private fun setTotalChapters() { - if (totalChapters != null) return - val mangaMap = allLibraryItems - totalChapters = mangaMap.map { - it.manga.id!! to db.getChapters(it.manga).executeAsBlocking().size - }.toMap() + return itemList.sortedWith(Comparator(sortFn)) } /** Gets the category by id @@ -388,89 +393,11 @@ class LibraryPresenter( * @param categoryId id of the categoty to get */ private fun getCategory(categoryId: Int): Category { - val category = categories.find { it.id == categoryId } ?: createDefaultCategory() - if (category.isFirst == null) { - category.isFirst = categories.minBy { it.order }?.id == category.id - } - if (category.isLast == null) category.isLast = categories.lastOrNull()?.id == category.id + val category = hashCategories[categoryId] ?: createDefaultCategory() + category.isAlone = categories.size <= 1 return category } - /** - * Sort 2 manga by the category's sorting - * - * @param i1 the first manga - * @param i2 the second manga to compare - * @param lastReadManga map of the last read of the library - */ - private fun sortCategory( - i1: LibraryItem, - i2: LibraryItem, - lastReadManga: Map - ): Int { - return if (i1.header.category.id == i2.header.category.id) { - val category = i1.header.category - if (category.mangaOrder.isNullOrEmpty() && category.mangaSort == null) { - category.changeSortTo(preferences.librarySortingMode().getOrDefault()) - if (category.id == 0) preferences.defaultMangaOrder() - .set(category.mangaSort.toString()) - else if (category.id ?: 0 > 0) db.insertCategory(category).executeAsBlocking() - } - i1.chapterCount = -1 - i2.chapterCount = -1 - val compare = when { - category.mangaSort != null -> { - var sort = when (category.sortingMode()) { - LibrarySort.ALPHA -> sortAlphabetical(i1, i2) - LibrarySort.LATEST_CHAPTER -> i2.manga.last_update.compareTo(i1.manga.last_update) - LibrarySort.UNREAD -> when { - i1.manga.unread == i2.manga.unread -> 0 - i1.manga.unread == 0 -> if (category.isAscending()) 1 else -1 - i2.manga.unread == 0 -> if (category.isAscending()) -1 else 1 - else -> i1.manga.unread.compareTo(i2.manga.unread) - } - LibrarySort.LAST_READ -> { - val manga1LastRead = lastReadManga[i1.manga.id!!] ?: lastReadManga.size - val manga2LastRead = lastReadManga[i2.manga.id!!] ?: lastReadManga.size - manga1LastRead.compareTo(manga2LastRead) - } - LibrarySort.TOTAL -> { - setTotalChapters() - val manga1TotalChapter = totalChapters!![i1.manga.id!!] ?: 0 - val mange2TotalChapter = totalChapters!![i2.manga.id!!] ?: 0 - i1.chapterCount = totalChapters!![i1.manga.id!!] ?: 0 - i2.chapterCount = totalChapters!![i2.manga.id!!] ?: 0 - manga1TotalChapter.compareTo(mange2TotalChapter) - } - LibrarySort.DATE_ADDED -> i2.manga.date_added.compareTo(i1.manga.date_added) - else -> sortAlphabetical(i1, i2) - } - if (!category.isAscending()) sort *= -1 - sort - } - category.mangaOrder.isNotEmpty() -> { - val order = category.mangaOrder - val index1 = order.indexOf(i1.manga.id!!) - val index2 = order.indexOf(i2.manga.id!!) - when { - index1 == index2 -> 0 - index1 == -1 -> -1 - index2 == -1 -> 1 - else -> index1.compareTo(index2) - } - } - else -> 0 - } - if (compare == 0) { - sortAlphabetical(i1, i2) - } else compare - } else { - val category = i1.header.category.order - val category2 = i2.header.category.order - category.compareTo(category2) - } - } - /** * Sort 2 manga by the their title (and remove articles if need be) * @@ -478,9 +405,11 @@ class LibraryPresenter( * @param i2 the second manga to compare */ private fun sortAlphabetical(i1: LibraryItem, i2: LibraryItem): Int { - return if (preferences.removeArticles().getOrDefault()) + return if (removeArticles) { i1.manga.title.removeArticles().compareTo(i2.manga.title.removeArticles(), true) - else i1.manga.title.compareTo(i2.manga.title, true) + } else { + i1.manga.title.compareTo(i2.manga.title, true) + } } /** @@ -489,6 +418,7 @@ class LibraryPresenter( * @return an list of all the manga in a itemized form. */ private fun getLibraryFromDB(): List { + removeArticles = preferences.removeArticles().getOrDefault() val categories = db.getCategories().executeAsBlocking().toMutableList() val showCategories = !preferences.hideCategories().getOrDefault() var libraryManga = db.getLibraryMangas().executeAsBlocking() @@ -550,6 +480,10 @@ class LibraryPresenter( this.categories = if (!showCategories) arrayListOf(categoryAll) else categories + hashCategories = HashMap(this.categories.mapNotNull { + it.id!! to it + }.toMap()) + return items } @@ -694,7 +628,7 @@ class LibraryPresenter( fun sortCategory(catId: Int, order: Int) { val category = categories.find { catId == it.id } ?: return category.mangaSort = ('a' + (order - 1)) - if (catId == -1) { + if (catId == -1 || category.isDynamic) { val sort = category.sortingMode() ?: LibrarySort.ALPHA preferences.librarySortingMode().set(sort) preferences.librarySortingAscending().set(category.isAscending()) @@ -709,6 +643,7 @@ class LibraryPresenter( fun rearrangeCategory(catId: Int?, mangaIds: List) { scope.launch { val category = categories.find { catId == it.id } ?: return@launch + if (category.isDynamic) return@launch category.mangaSort = null category.mangaOrder = mangaIds if (category.id == 0) preferences.defaultMangaOrder().set(mangaIds.joinToString("/")) @@ -726,6 +661,7 @@ class LibraryPresenter( scope.launch { val categoryId = catId ?: return@launch val category = categories.find { catId == it.id } ?: return@launch + if (category.isDynamic) return@launch val oldCatId = manga.category manga.category = categoryId @@ -762,7 +698,7 @@ class LibraryPresenter( } fun toggleCategoryVisibility(categoryId: Int) { - if (categoryId <= -1) return + if (categoryId <= -1 || categories.find { it.id == categoryId }?.isDynamic == true) return val categoriesHidden = preferences.collapsedCategories().getOrDefault().mapNotNull { it.toIntOrNull() }.toMutableSet() diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt index bbbff6aa13..52a6713fff 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/lang/StringExtensions.kt @@ -14,7 +14,12 @@ fun String.chop(count: Int, replacement: String = "..."): String { } fun String.removeArticles(): String { - return this.replace(Regex("^(an|a|the) ", RegexOption.IGNORE_CASE), "") + return when { + startsWith("a ", true) -> substring(2) + startsWith("an ", true) -> substring(3) + startsWith("the ", true) -> substring(4) + else -> this + } } /**