diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt index 652cf6e214..7604dc159a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt @@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.extension.ExtensionManager import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.util.chapter.ChapterFilter import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import uy.kohesive.injekt.api.InjektModule @@ -48,6 +49,8 @@ class AppModule(val app: Application) : InjektModule { addSingletonFactory { Gson() } + addSingletonFactory { ChapterFilter() } + // Asynchronously init expensive components for a faster cold start GlobalScope.launch { get() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt index 66757144c2..4618907fa3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt @@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterAdapter import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem +import eu.kanade.tachiyomi.util.chapter.ChapterUtil import uy.kohesive.injekt.injectLazy import java.text.DecimalFormat import java.text.DecimalFormatSymbols @@ -26,7 +27,7 @@ class MangaDetailsAdapter( val presenter = controller.presenter val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols() - .apply { decimalSeparator = '.' }) + .apply { decimalSeparator = '.' }) fun setChapters(items: List?) { this.items = items ?: emptyList() @@ -46,8 +47,10 @@ class MangaDetailsAdapter( if (s.isNullOrBlank()) { updateDataSet(items) } else { - updateDataSet(items.filter { it.name.contains(s, true) || - it.scanlator?.contains(s, true) == true }) + updateDataSet(items.filter { + it.name.contains(s, true) || + it.scanlator?.contains(s, true) == true + }) } } @@ -64,7 +67,7 @@ class MangaDetailsAdapter( getItem(position) as? ChapterItem ?: return recyclerView.context.getString(R.string.top) return when (val scrollType = presenter.scrollType) { MangaDetailsPresenter.MULTIPLE_VOLUMES, MangaDetailsPresenter.MULTIPLE_SEASONS -> { - val volume = presenter.getGroupNumber(chapter) + val volume = ChapterUtil.getGroupNumber(chapter) if (volume != null) { recyclerView.context.getString( if (scrollType == MangaDetailsPresenter.MULTIPLE_SEASONS) R.string.season_ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 3634df9064..a2818d0fa1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -30,6 +30,8 @@ import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate +import eu.kanade.tachiyomi.util.chapter.ChapterFilter +import eu.kanade.tachiyomi.util.chapter.ChapterUtil import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.lang.trimOrNull import eu.kanade.tachiyomi.util.storage.DiskUtil @@ -57,7 +59,8 @@ class MangaDetailsPresenter( val preferences: PreferencesHelper = Injekt.get(), val coverCache: CoverCache = Injekt.get(), private val db: DatabaseHelper = Injekt.get(), - private val downloadManager: DownloadManager = Injekt.get() + private val downloadManager: DownloadManager = Injekt.get(), + private val chapterFilter: ChapterFilter = Injekt.get() ) : DownloadQueue.DownloadListener, LibraryServiceListener { private var scope = CoroutineScope(Job() + Dispatchers.Default) @@ -68,8 +71,6 @@ class MangaDetailsPresenter( var hasRequested = false var isLoading = false var scrollType = 0 - private val volumeRegex = Regex("""(vol|volume)\.? *([0-9]+)?""", RegexOption.IGNORE_CASE) - private val seasonRegex = Regex("""(Season |S)([0-9]+)?""") private val loggedServices by lazy { Injekt.get().services.filter { it.isLogged } } private var tracks = emptyList() @@ -199,26 +200,6 @@ class MangaDetailsPresenter( controller.refreshAdapter() } - /** - * Whether the display only downloaded filter is enabled. - */ - fun onlyDownloaded() = manga.downloadedFilter == Manga.SHOW_DOWNLOADED - - /** - * Whether the display only downloaded filter is enabled. - */ - fun onlyBookmarked() = manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED - - /** - * Whether the display only unread filter is enabled. - */ - fun onlyUnread() = manga.readFilter == Manga.SHOW_UNREAD - - /** - * Whether the display only read filter is enabled. - */ - fun onlyRead() = manga.readFilter == Manga.SHOW_READ - /** * Whether the sorting method is descending or ascending. */ @@ -232,18 +213,9 @@ class MangaDetailsPresenter( private fun applyChapterFilters(chapterList: List): List { if (isLockedFromSearch) return chapterList - var chapters = chapterList - if (onlyUnread()) { - chapters = chapters.filter { !it.read } - } else if (onlyRead()) { - chapters = chapters.filter { it.read } - } - if (onlyDownloaded()) { - chapters = chapters.filter { it.isDownloaded || it.manga.source == LocalSource.ID } - } - if (onlyBookmarked()) { - chapters = chapters.filter { it.bookmark } - } + + val chapters = chapterFilter.filterChapters(chapterList, manga) as List + val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { Manga.SORTING_SOURCE -> when (sortDescending()) { true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } @@ -255,68 +227,19 @@ class MangaDetailsPresenter( } else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } } - chapters = chapters.sortedWith(Comparator(sortFunction)) getScrollType(chapters) - return chapters + return chapters.sortedWith(Comparator(sortFunction)) } private fun getScrollType(chapters: List) { scrollType = when { - hasMultipleVolumes(chapters) -> MULTIPLE_VOLUMES - hasMultipleSeasons(chapters) -> MULTIPLE_SEASONS - hasTensOfChapters(chapters) -> TENS_OF_CHAPTERS + ChapterUtil.hasMultipleVolumes(chapters) -> MULTIPLE_VOLUMES + ChapterUtil.hasMultipleSeasons(chapters) -> MULTIPLE_SEASONS + ChapterUtil.hasTensOfChapters(chapters) -> TENS_OF_CHAPTERS else -> 0 } } - fun getGroupNumber(chapter: ChapterItem): Int? { - val groups = volumeRegex.find(chapter.name)?.groups - if (groups != null) return groups[2]?.value?.toIntOrNull() - val seasonGroups = seasonRegex.find(chapter.name)?.groups - if (seasonGroups != null) return seasonGroups[2]?.value?.toIntOrNull() - return null - } - - private fun getVolumeNumber(chapter: ChapterItem): Int? { - val groups = volumeRegex.find(chapter.name)?.groups - if (groups != null) return groups[2]?.value?.toIntOrNull() - return null - } - - private fun getSeasonNumber(chapter: ChapterItem): Int? { - val groups = seasonRegex.find(chapter.name)?.groups - if (groups != null) return groups[2]?.value?.toIntOrNull() - return null - } - - private fun hasMultipleVolumes(chapters: List): Boolean { - val volumeSet = mutableSetOf() - chapters.forEach { - val volNum = getVolumeNumber(it) - if (volNum != null) { - volumeSet.add(volNum) - if (volumeSet.size >= 2) return true - } - } - return false - } - - private fun hasMultipleSeasons(chapters: List): Boolean { - val volumeSet = mutableSetOf() - chapters.forEach { - val volNum = getSeasonNumber(it) - if (volNum != null) { - volumeSet.add(volNum) - if (volumeSet.size >= 2) return true - } - } - return false - } - - private fun hasTensOfChapters(chapters: List): Boolean { - return chapters.size > 20 - } - /** * Returns the next unread chapter or null if everything is read. */ @@ -596,10 +519,10 @@ class MangaDetailsPresenter( fun currentFilters(): String { val filtersId = mutableListOf() - filtersId.add(if (onlyRead()) R.string.read else null) - filtersId.add(if (onlyUnread()) R.string.unread else null) - filtersId.add(if (onlyDownloaded()) R.string.downloaded else null) - filtersId.add(if (onlyBookmarked()) R.string.bookmarked else null) + filtersId.add(if (manga.readFilter == Manga.SHOW_READ) R.string.read else null) + filtersId.add(if (manga.readFilter == Manga.SHOW_UNREAD) R.string.unread else null) + filtersId.add(if (manga.downloadedFilter == Manga.SHOW_DOWNLOADED) R.string.downloaded else null) + filtersId.add(if (manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED) R.string.bookmarked else null) return filtersId.filterNotNull().joinToString(", ") { preferences.context.getString(it) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderChapterFilter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderChapterFilter.kt deleted file mode 100644 index 86317a12cb..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderChapterFilter.kt +++ /dev/null @@ -1,64 +0,0 @@ -package eu.kanade.tachiyomi.ui.reader - -import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.models.Manga -import eu.kanade.tachiyomi.data.download.DownloadManager -import eu.kanade.tachiyomi.data.preference.PreferencesHelper - -/** - * This class filters chapters for the reader based on the user enabled preferences and filters - */ -class ReaderChapterFilter( - private val downloadManager: DownloadManager, - private val preferences: PreferencesHelper -) { - - fun filterChapter( - dbChapters: List, - manga: Manga, - selectedChapter: Chapter? = null - ): List { - - // if neither preference is enabled don't even filter - if (!preferences.skipRead() && !preferences.skipFiltered()) { - return dbChapters - } - - var filteredChapters = dbChapters - if (preferences.skipRead()) { - filteredChapters = filteredChapters.filter { !it.read } - } - if (preferences.skipFiltered()) { - val readEnabled = manga.readFilter == Manga.SHOW_READ - val unreadEnabled = manga.readFilter == Manga.SHOW_UNREAD - val downloadEnabled = manga.downloadedFilter == Manga.SHOW_DOWNLOADED - val bookmarkEnabled = manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED - - // if none of the filters are enabled skip the filtering of them - if (readEnabled || unreadEnabled || downloadEnabled || bookmarkEnabled) { - - filteredChapters = filteredChapters.filter { - if (readEnabled && it.read.not() || - (unreadEnabled && it.read) || - (bookmarkEnabled && it.bookmark.not()) || - (downloadEnabled && downloadManager.isChapterDownloaded(it, manga).not()) - ) { - return@filter false - } - return@filter true - } - } - } - - // add the selected chapter to the list in case it was filtered out - if (selectedChapter?.id != null) { - val find = filteredChapters.find { it.id == selectedChapter.id } - if (find == null) { - val mutableList = filteredChapters.toMutableList() - mutableList.add(selectedChapter) - filteredChapters = mutableList.toList() - } - } - return filteredChapters - } -} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt index 2c95e34467..be31699039 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPresenter.kt @@ -27,6 +27,7 @@ import eu.kanade.tachiyomi.ui.reader.loader.DownloadPageLoader import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters +import eu.kanade.tachiyomi.util.chapter.ChapterFilter import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.system.ImageUtil import kotlinx.coroutines.Dispatchers @@ -53,11 +54,10 @@ class ReaderPresenter( private val sourceManager: SourceManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(), private val coverCache: CoverCache = Injekt.get(), - private val preferences: PreferencesHelper = Injekt.get() + private val preferences: PreferencesHelper = Injekt.get(), + private val chapterFilter: ChapterFilter = Injekt.get() ) : BasePresenter() { - private val readerChapterFilter = ReaderChapterFilter(downloadManager, preferences) - /** * The manga loaded in the reader. It can be null when instantiated for a short time. */ @@ -101,7 +101,7 @@ class ReaderPresenter( ?: error("Requested chapter of id $chapterId not found in chapter list") val chaptersForReader = - readerChapterFilter.filterChapter(dbChapters, manga, selectedChapter) + chapterFilter.filterChaptersForReader(dbChapters, manga, selectedChapter) when (manga.sorting) { Manga.SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader) @@ -187,7 +187,7 @@ class ReaderPresenter( chapterItems = withContext(Dispatchers.IO) { val dbChapters = db.getChapters(manga).executeAsBlocking() val list = - readerChapterFilter.filterChapter(dbChapters, manga, getCurrentChapter()?.chapter) + chapterFilter.filterChaptersForReader(dbChapters, manga, getCurrentChapter()?.chapter) .sortedBy { when (manga.sorting) { Manga.SORTING_NUMBER -> it.chapter_number diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilter.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilter.kt new file mode 100644 index 0000000000..6ee06e61bb --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterFilter.kt @@ -0,0 +1,62 @@ +package eu.kanade.tachiyomi.util.chapter + +import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.data.database.models.Manga +import eu.kanade.tachiyomi.data.download.DownloadManager +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import uy.kohesive.injekt.Injekt +import uy.kohesive.injekt.api.get + +class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downloadManager: DownloadManager = Injekt.get()) { + + // filters chapters based on the manga values + fun filterChapters(chapters: List, manga: Manga): List { + val readEnabled = manga.readFilter == Manga.SHOW_READ + val unreadEnabled = manga.readFilter == Manga.SHOW_UNREAD + val downloadEnabled = manga.downloadedFilter == Manga.SHOW_DOWNLOADED + val bookmarkEnabled = manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED + + // if none of the filters are enabled skip the filtering of them + return if (readEnabled || unreadEnabled || downloadEnabled || bookmarkEnabled) { + chapters.filter { + if (readEnabled && it.read.not() || + (unreadEnabled && it.read) || + (bookmarkEnabled && it.bookmark.not()) || + (downloadEnabled && downloadManager.isChapterDownloaded(it, manga).not()) + ) { + return@filter false + } + return@filter true + } + } else { + chapters + } + } + + // filter chapters for the reader + fun filterChaptersForReader(chapters: List, manga: Manga, selectedChapter: Chapter? = null): List { + // if neither preference is enabled don't even filter + if (!preferences.skipRead() && !preferences.skipFiltered()) { + return chapters + } + + var filteredChapters = chapters + if (preferences.skipRead()) { + filteredChapters = filteredChapters.filter { !it.read } + } + if (preferences.skipFiltered()) { + filteredChapters = filterChapters(filteredChapters, manga) + } + + // add the selected chapter to the list in case it was filtered out + if (selectedChapter?.id != null) { + val find = filteredChapters.find { it.id == selectedChapter.id } + if (find == null) { + val mutableList = filteredChapters.toMutableList() + mutableList.add(selectedChapter) + filteredChapters = mutableList.toList() + } + } + return filteredChapters + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterUtil.kt index 8397aba3f9..2a1ceadbbf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterUtil.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterUtil.kt @@ -3,10 +3,10 @@ package eu.kanade.tachiyomi.util.chapter import android.content.Context import android.content.res.ColorStateList import android.widget.TextView -import androidx.core.graphics.ColorUtils import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Chapter +import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.util.system.contextCompatColor import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPxEnd @@ -84,6 +84,55 @@ class ChapterUtil { private fun bookmarkedColor(context: Context): Int = context.contextCompatColor(R.color.bookmarked_chapter) - private fun bookmarkedAndReadColor(context: Context): Int = ColorUtils.setAlphaComponent(context.contextCompatColor(R.color.bookmarked_chapter), 150) + private val volumeRegex = Regex("""(vol|volume)\.? *([0-9]+)?""", RegexOption.IGNORE_CASE) + private val seasonRegex = Regex("""(Season |S)([0-9]+)?""") + + fun getGroupNumber(chapter: Chapter): Int? { + val groups = volumeRegex.find(chapter.name)?.groups + if (groups != null) return groups[2]?.value?.toIntOrNull() + val seasonGroups = seasonRegex.find(chapter.name)?.groups + if (seasonGroups != null) return seasonGroups[2]?.value?.toIntOrNull() + return null + } + + private fun getVolumeNumber(chapter: Chapter): Int? { + val groups = volumeRegex.find(chapter.name)?.groups + if (groups != null) return groups[2]?.value?.toIntOrNull() + return null + } + + private fun getSeasonNumber(chapter: Chapter): Int? { + val groups = seasonRegex.find(chapter.name)?.groups + if (groups != null) return groups[2]?.value?.toIntOrNull() + return null + } + + fun hasMultipleVolumes(chapters: List): Boolean { + val volumeSet = mutableSetOf() + chapters.forEach { + val volNum = getVolumeNumber(it) + if (volNum != null) { + volumeSet.add(volNum) + if (volumeSet.size >= 2) return true + } + } + return false + } + + fun hasMultipleSeasons(chapters: List): Boolean { + val volumeSet = mutableSetOf() + chapters.forEach { + val volNum = getSeasonNumber(it) + if (volNum != null) { + volumeSet.add(volNum) + if (volumeSet.size >= 2) return true + } + } + return false + } + + fun hasTensOfChapters(chapters: List): Boolean { + return chapters.size > 20 + } } }