Use the same logic in the mangadetails filter as the reader filter (#543)

* use the same chapter filter for the mangadetails and the reader presenter page

* removed unused class

* move volume detection logic to chapter utils
This commit is contained in:
Carlos 2020-08-09 14:58:12 -04:00 committed by GitHub
parent 813a3c6e68
commit 7372109520
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 143 additions and 167 deletions

View File

@ -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<PreferencesHelper>() }

View File

@ -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<ChapterItem>?) {
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_

View File

@ -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<TrackManager>().services.filter { it.isLogged } }
private var tracks = emptyList<Track>()
@ -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<ChapterItem>): List<ChapterItem> {
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<ChapterItem>
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<ChapterItem>) {
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<ChapterItem>): Boolean {
val volumeSet = mutableSetOf<Int>()
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<ChapterItem>): Boolean {
val volumeSet = mutableSetOf<Int>()
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<ChapterItem>): 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<Int?>()
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) }
}

View File

@ -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<Chapter>,
manga: Manga,
selectedChapter: Chapter? = null
): List<Chapter> {
// 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
}
}

View File

@ -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<ReaderActivity>() {
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

View File

@ -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<Chapter>, manga: Manga): List<Chapter> {
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<Chapter>, manga: Manga, selectedChapter: Chapter? = null): List<Chapter> {
// 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
}
}

View File

@ -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<Chapter>): Boolean {
val volumeSet = mutableSetOf<Int>()
chapters.forEach {
val volNum = getVolumeNumber(it)
if (volNum != null) {
volumeSet.add(volNum)
if (volumeSet.size >= 2) return true
}
}
return false
}
fun hasMultipleSeasons(chapters: List<Chapter>): Boolean {
val volumeSet = mutableSetOf<Int>()
chapters.forEach {
val volNum = getSeasonNumber(it)
if (volNum != null) {
volumeSet.add(volNum)
if (volumeSet.size >= 2) return true
}
}
return false
}
fun hasTensOfChapters(chapters: List<ChapterItem>): Boolean {
return chapters.size > 20
}
}
}