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 35cf30c67a..5aebcb3ce1 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 @@ -2,7 +2,14 @@ package eu.kanade.tachiyomi.data.database.models class LibraryManga : MangaImpl() { - var unread: Int = 0 + var unreadCount: Int = 0 + var readCount: Int = 0 + + val totalChapters + get() = readCount + unreadCount + + val hasStarted + get() = readCount > 0 var category: Int = 0 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt index 57091cd07b..6c1a424a06 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt @@ -8,21 +8,28 @@ import eu.kanade.tachiyomi.data.database.tables.MangaCategoryTable as MangaCateg import eu.kanade.tachiyomi.data.database.tables.MangaTable as Manga /** - * Query to get the manga from the library, with their categories and unread count. + * Query to get the manga from the library, with their categories, read and unread count. */ val libraryQuery = """ SELECT M.*, COALESCE(MC.${MangaCategory.COL_CATEGORY_ID}, 0) AS ${Manga.COL_CATEGORY} FROM ( - SELECT ${Manga.TABLE}.*, COALESCE(C.unread, 0) AS ${Manga.COL_UNREAD} + SELECT ${Manga.TABLE}.*, COALESCE(C.unreadCount, 0) AS ${Manga.COMPUTED_COL_UNREAD_COUNT}, COALESCE(R.readCount, 0) AS ${Manga.COMPUTED_COL_READ_COUNT} FROM ${Manga.TABLE} LEFT JOIN ( - SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS unread + SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS unreadCount FROM ${Chapter.TABLE} WHERE ${Chapter.COL_READ} = 0 GROUP BY ${Chapter.COL_MANGA_ID} ) AS C ON ${Manga.COL_ID} = C.${Chapter.COL_MANGA_ID} + LEFT JOIN ( + SELECT ${Chapter.COL_MANGA_ID}, COUNT(*) AS readCount + FROM ${Chapter.TABLE} + WHERE ${Chapter.COL_READ} = 1 + GROUP BY ${Chapter.COL_MANGA_ID} + ) AS R + ON ${Manga.COL_ID} = R.${Chapter.COL_MANGA_ID} WHERE ${Manga.COL_FAVORITE} = 1 GROUP BY ${Manga.COL_ID} ORDER BY ${Manga.COL_TITLE} 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 998c141fb9..2435b80cd0 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 @@ -16,8 +16,9 @@ class LibraryMangaGetResolver : DefaultGetResolver(), BaseMangaGet val manga = LibraryManga() mapBaseFromCursor(manga, cursor) - manga.unread = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_UNREAD)) + manga.unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_UNREAD_COUNT)) manga.category = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COL_CATEGORY)) + manga.readCount = cursor.getInt(cursor.getColumnIndexOrThrow(MangaTable.COMPUTED_COL_READ_COUNT)) return manga } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt index 013e683e03..fe9e6845f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/tables/MangaTable.kt @@ -39,12 +39,15 @@ object MangaTable { const val COL_CHAPTER_FLAGS = "chapter_flags" - const val COL_UNREAD = "unread" - const val COL_CATEGORY = "category" const val COL_COVER_LAST_MODIFIED = "cover_last_modified" + // Not an actual value but computed when created + const val COMPUTED_COL_UNREAD_COUNT = "unread_count" + + const val COMPUTED_COL_READ_COUNT = "read_count" + val createTableQuery: String get() = """CREATE TABLE $TABLE( diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt index 76ff9dc8b1..209323527d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt @@ -21,6 +21,7 @@ import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Companion.start import eu.kanade.tachiyomi.data.notification.Notifications import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING +import eu.kanade.tachiyomi.data.preference.MANGA_STARTED import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.TrackManager @@ -266,7 +267,14 @@ class LibraryUpdateService( listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED } } if (MANGA_FULLY_READ in restrictions) { - listToUpdate = listToUpdate.filter { it.unread == 0 } + listToUpdate = listToUpdate.filter { it.unreadCount == 0 } + } + if (MANGA_STARTED in restrictions) { + listToUpdate = listToUpdate.filter { manga -> + // If the manga has 0 chapters you can't actually start reading it + if (manga.totalChapters == 0) true + else manga.hasStarted + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 0f1194965b..026f40722f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -31,6 +31,8 @@ object PreferenceKeys { const val filterUnread = "pref_filter_library_unread" + const val filterStarted = "pref_filter_library_started" + const val filterCompleted = "pref_filter_library_completed" const val filterTracked = "pref_filter_library_tracked" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt index d49eebc34d..56a0180021 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceValues.kt @@ -7,6 +7,7 @@ const val DEVICE_CHARGING = "ac" const val MANGA_ONGOING = "manga_ongoing" const val MANGA_FULLY_READ = "manga_fully_read" +const val MANGA_STARTED = "manga_started" /** * This class stores the values for the preferences in the application. diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 911ec8e826..14d2cca0d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -224,7 +224,7 @@ class PreferencesHelper(val context: Context) { fun libraryUpdateInterval() = flowPrefs.getInt("pref_library_update_interval_key", 24) fun libraryUpdateDeviceRestriction() = flowPrefs.getStringSet("library_update_restriction", setOf(DEVICE_ONLY_ON_WIFI)) - fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_FULLY_READ, MANGA_ONGOING)) + fun libraryUpdateMangaRestriction() = flowPrefs.getStringSet("library_update_manga_restriction", setOf(MANGA_FULLY_READ, MANGA_ONGOING, MANGA_STARTED)) fun showUpdatesNavBadge() = flowPrefs.getBoolean("library_update_show_tab_badge", false) fun unreadUpdatesCount() = flowPrefs.getInt("library_unread_updates_count", 0) @@ -252,6 +252,8 @@ class PreferencesHelper(val context: Context) { fun filterUnread() = flowPrefs.getInt(Keys.filterUnread, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) + fun filterStarted() = flowPrefs.getInt(Keys.filterStarted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) + fun filterCompleted() = flowPrefs.getInt(Keys.filterCompleted, ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) fun filterTracking(name: Int) = flowPrefs.getInt("${Keys.filterTracked}_$name", ExtendedNavigationView.Item.TriStateGroup.State.IGNORE.value) 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 60f61bd0fb..f4068a6015 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 @@ -120,6 +120,7 @@ class LibraryPresenter( val downloadedOnly = preferences.downloadedOnly().get() val filterDownloaded = preferences.filterDownloaded().get() val filterUnread = preferences.filterUnread().get() + val filterStarted = preferences.filterStarted().get() val filterCompleted = preferences.filterCompleted().get() val loggedInServices = trackManager.services.filter { trackService -> trackService.isLogged } .associate { trackService -> @@ -127,22 +128,6 @@ class LibraryPresenter( } val isNotAnyLoggedIn = !loggedInServices.values.any() - val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item -> - if (filterUnread == State.IGNORE.value) return@unread true - val isUnread = item.manga.unread != 0 - - return@unread if (filterUnread == State.INCLUDE.value) isUnread - else !isUnread - } - - val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item -> - if (filterCompleted == State.IGNORE.value) return@completed true - val isCompleted = item.manga.status == SManga.COMPLETED - - return@completed if (filterCompleted == State.INCLUDE.value) isCompleted - else !isCompleted - } - val filterFnDownloaded: (LibraryItem) -> Boolean = downloaded@{ item -> if (!downloadedOnly && filterDownloaded == State.IGNORE.value) return@downloaded true val isDownloaded = when { @@ -155,6 +140,30 @@ class LibraryPresenter( else !isDownloaded } + val filterFnUnread: (LibraryItem) -> Boolean = unread@{ item -> + if (filterUnread == State.IGNORE.value) return@unread true + val isUnread = item.manga.unreadCount != 0 + + return@unread if (filterUnread == State.INCLUDE.value) isUnread + else !isUnread + } + + val filterFnStarted: (LibraryItem) -> Boolean = started@{ item -> + if (filterStarted == State.IGNORE.value) return@started true + val hasStarted = item.manga.hasStarted + + return@started if (filterStarted == State.INCLUDE.value) hasStarted + else !hasStarted + } + + val filterFnCompleted: (LibraryItem) -> Boolean = completed@{ item -> + if (filterCompleted == State.IGNORE.value) return@completed true + val isCompleted = item.manga.status == SManga.COMPLETED + + return@completed if (filterCompleted == State.INCLUDE.value) isCompleted + else !isCompleted + } + val filterFnTracking: (LibraryItem) -> Boolean = tracking@{ item -> if (isNotAnyLoggedIn) return@tracking true @@ -181,9 +190,10 @@ class LibraryPresenter( val filterFn: (LibraryItem) -> Boolean = filter@{ item -> return@filter !( - !filterFnUnread(item) || + !filterFnDownloaded(item) || + !filterFnUnread(item) || + !filterFnStarted(item) || !filterFnCompleted(item) || - !filterFnDownloaded(item) || !filterFnTracking(item) ) } @@ -212,7 +222,7 @@ class LibraryPresenter( } item.unreadCount = if (showUnreadBadges) { - item.manga.unread + item.manga.unreadCount } else { // Unset unread count if not enabled -1 @@ -245,10 +255,6 @@ class LibraryPresenter( var counter = 0 db.getLastReadManga().executeAsBlocking().associate { it.id!! to counter++ } } - val totalChapterManga by lazy { - var counter = 0 - db.getTotalChapterManga().executeAsBlocking().associate { it.id!! to counter++ } - } val latestChapterManga by lazy { var counter = 0 db.getLatestChapterManga().executeAsBlocking().associate { it.id!! to counter++ } @@ -262,7 +268,7 @@ class LibraryPresenter( (category.id ?: 0) to SortModeSetting.get(preferences, category) } - val sortAscending = categories.associate { category -> + val sortDirections = categories.associate { category -> (category.id ?: 0) to SortDirectionSetting.get(preferences, category) } @@ -272,7 +278,7 @@ class LibraryPresenter( } val sortFn: (LibraryItem, LibraryItem) -> Int = { i1, i2 -> val sortingMode = sortingModes[i1.manga.category]!! - val sortAscending = sortAscending[i1.manga.category]!! == SortDirectionSetting.ASCENDING + val sortAscending = sortDirections[i1.manga.category]!! == SortDirectionSetting.ASCENDING when (sortingMode) { SortModeSetting.ALPHABETICAL -> { collator.compare(i1.manga.title.lowercase(locale), i2.manga.title.lowercase(locale)) @@ -286,15 +292,13 @@ class LibraryPresenter( SortModeSetting.LAST_CHECKED -> i2.manga.last_update.compareTo(i1.manga.last_update) SortModeSetting.UNREAD -> when { // Ensure unread content comes first - i1.manga.unread == i2.manga.unread -> 0 - i1.manga.unread == 0 -> if (sortAscending) 1 else -1 - i2.manga.unread == 0 -> if (sortAscending) -1 else 1 - else -> i1.manga.unread.compareTo(i2.manga.unread) + i1.manga.unreadCount == i2.manga.unreadCount -> 0 + i1.manga.unreadCount == 0 -> if (sortAscending) 1 else -1 + i2.manga.unreadCount == 0 -> if (sortAscending) -1 else 1 + else -> i1.manga.unreadCount.compareTo(i2.manga.unreadCount) } SortModeSetting.TOTAL_CHAPTERS -> { - val manga1TotalChapter = totalChapterManga[i1.manga.id!!] ?: 0 - val mange2TotalChapter = totalChapterManga[i2.manga.id!!] ?: 0 - manga1TotalChapter.compareTo(mange2TotalChapter) + i1.manga.totalChapters.compareTo(i2.manga.totalChapters) } SortModeSetting.LATEST_CHAPTER -> { val manga1latestChapter = latestChapterManga[i1.manga.id!!] @@ -315,7 +319,7 @@ class LibraryPresenter( } return map.mapValues { entry -> - val sortAscending = sortAscending[entry.key]!! == SortDirectionSetting.ASCENDING + val sortAscending = sortDirections[entry.key]!! == SortDirectionSetting.ASCENDING val comparator = if (sortAscending) { Comparator(sortFn) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt index 661a8611bc..bf7afb633e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibrarySettingsSheet.kt @@ -89,6 +89,7 @@ class LibrarySettingsSheet( private val downloaded = Item.TriStateGroup(R.string.action_filter_downloaded, this) private val unread = Item.TriStateGroup(R.string.action_filter_unread, this) + private val started = Item.TriStateGroup(R.string.action_filter_started, this) private val completed = Item.TriStateGroup(R.string.completed, this) private val trackFilters: Map @@ -103,7 +104,7 @@ class LibrarySettingsSheet( trackFilters = services.associate { service -> Pair(service.id, Item.TriStateGroup(getServiceResId(service, size), this)) } - val list: MutableList = mutableListOf(downloaded, unread, completed) + val list: MutableList = mutableListOf(downloaded, unread, started, completed) if (size > 1) list.add(Item.Header(R.string.action_filter_tracked)) list.addAll(trackFilters.values) items = list @@ -122,6 +123,7 @@ class LibrarySettingsSheet( downloaded.state = preferences.filterDownloaded().get() } unread.state = preferences.filterUnread().get() + started.state = preferences.filterStarted().get() completed.state = preferences.filterCompleted().get() trackFilters.forEach { trackFilter -> @@ -141,6 +143,7 @@ class LibrarySettingsSheet( when (item) { downloaded -> preferences.filterDownloaded().set(newState) unread -> preferences.filterUnread().set(newState) + started -> preferences.filterStarted().set(newState) completed -> preferences.filterCompleted().set(newState) else -> { trackFilters.forEach { trackFilter -> diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt index 3ef5a17958..d594b8f924 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt @@ -15,6 +15,7 @@ import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI import eu.kanade.tachiyomi.data.preference.MANGA_FULLY_READ import eu.kanade.tachiyomi.data.preference.MANGA_ONGOING +import eu.kanade.tachiyomi.data.preference.MANGA_STARTED import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.databinding.PrefLibraryColumnsBinding @@ -195,16 +196,16 @@ class SettingsLibraryController : SettingsController() { multiSelectListPreference { bindTo(preferences.libraryUpdateMangaRestriction()) titleRes = R.string.pref_library_update_manga_restriction - entriesRes = arrayOf(R.string.pref_update_only_completely_read, R.string.pref_update_only_non_completed) - entryValues = arrayOf(MANGA_FULLY_READ, MANGA_ONGOING) + entriesRes = arrayOf(R.string.pref_update_only_completely_read, R.string.pref_update_only_non_completed, R.string.pref_update_only_started) + entryValues = arrayOf(MANGA_FULLY_READ, MANGA_ONGOING, MANGA_STARTED) fun updateSummary() { - val restrictions = preferences.libraryUpdateMangaRestriction().get() - .sorted() + val restrictions = preferences.libraryUpdateMangaRestriction().get().sorted() .map { when (it) { MANGA_ONGOING -> context.getString(R.string.pref_update_only_non_completed) MANGA_FULLY_READ -> context.getString(R.string.pref_update_only_completely_read) + MANGA_STARTED -> context.getString(R.string.pref_update_only_started) else -> it } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7de6308ee1..df5b6f704d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ Bookmarked Tracked Unread + Started Remove filter Alphabetically Total manga @@ -222,10 +223,12 @@ Only on Wi-Fi Charging Restrictions: %s + Library update restrictions Only update: %s Completely read series Ongoing series + Started series Show unread count on Updates icon Automatically refresh metadata Check for new cover and details when updating library