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.extension.ExtensionManager
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
@ -48,6 +49,8 @@ class AppModule(val app: Application) : InjektModule {
addSingletonFactory { Gson() } addSingletonFactory { Gson() }
addSingletonFactory { ChapterFilter() }
// Asynchronously init expensive components for a faster cold start // Asynchronously init expensive components for a faster cold start
GlobalScope.launch { get<PreferencesHelper>() } 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.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterAdapter import eu.kanade.tachiyomi.ui.manga.chapter.BaseChapterAdapter
import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem import eu.kanade.tachiyomi.ui.manga.chapter.ChapterItem
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.DecimalFormatSymbols import java.text.DecimalFormatSymbols
@ -26,7 +27,7 @@ class MangaDetailsAdapter(
val presenter = controller.presenter val presenter = controller.presenter
val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols() val decimalFormat = DecimalFormat("#.###", DecimalFormatSymbols()
.apply { decimalSeparator = '.' }) .apply { decimalSeparator = '.' })
fun setChapters(items: List<ChapterItem>?) { fun setChapters(items: List<ChapterItem>?) {
this.items = items ?: emptyList() this.items = items ?: emptyList()
@ -46,8 +47,10 @@ class MangaDetailsAdapter(
if (s.isNullOrBlank()) { if (s.isNullOrBlank()) {
updateDataSet(items) updateDataSet(items)
} else { } else {
updateDataSet(items.filter { it.name.contains(s, true) || updateDataSet(items.filter {
it.scanlator?.contains(s, true) == true }) 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) getItem(position) as? ChapterItem ?: return recyclerView.context.getString(R.string.top)
return when (val scrollType = presenter.scrollType) { return when (val scrollType = presenter.scrollType) {
MangaDetailsPresenter.MULTIPLE_VOLUMES, MangaDetailsPresenter.MULTIPLE_SEASONS -> { MangaDetailsPresenter.MULTIPLE_VOLUMES, MangaDetailsPresenter.MULTIPLE_SEASONS -> {
val volume = presenter.getGroupNumber(chapter) val volume = ChapterUtil.getGroupNumber(chapter)
if (volume != null) { if (volume != null) {
recyclerView.context.getString( recyclerView.context.getString(
if (scrollType == MangaDetailsPresenter.MULTIPLE_SEASONS) R.string.season_ 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.chapter.ChapterItem
import eu.kanade.tachiyomi.ui.manga.track.TrackItem import eu.kanade.tachiyomi.ui.manga.track.TrackItem
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate 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.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.lang.trimOrNull import eu.kanade.tachiyomi.util.lang.trimOrNull
import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.DiskUtil
@ -57,7 +59,8 @@ class MangaDetailsPresenter(
val preferences: PreferencesHelper = Injekt.get(), val preferences: PreferencesHelper = Injekt.get(),
val coverCache: CoverCache = Injekt.get(), val coverCache: CoverCache = Injekt.get(),
private val db: DatabaseHelper = 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 { ) : DownloadQueue.DownloadListener, LibraryServiceListener {
private var scope = CoroutineScope(Job() + Dispatchers.Default) private var scope = CoroutineScope(Job() + Dispatchers.Default)
@ -68,8 +71,6 @@ class MangaDetailsPresenter(
var hasRequested = false var hasRequested = false
var isLoading = false var isLoading = false
var scrollType = 0 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 val loggedServices by lazy { Injekt.get<TrackManager>().services.filter { it.isLogged } }
private var tracks = emptyList<Track>() private var tracks = emptyList<Track>()
@ -199,26 +200,6 @@ class MangaDetailsPresenter(
controller.refreshAdapter() 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. * Whether the sorting method is descending or ascending.
*/ */
@ -232,18 +213,9 @@ class MangaDetailsPresenter(
private fun applyChapterFilters(chapterList: List<ChapterItem>): List<ChapterItem> { private fun applyChapterFilters(chapterList: List<ChapterItem>): List<ChapterItem> {
if (isLockedFromSearch) if (isLockedFromSearch)
return chapterList return chapterList
var chapters = chapterList
if (onlyUnread()) { val chapters = chapterFilter.filterChapters(chapterList, manga) as List<ChapterItem>
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 sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) { val sortFunction: (Chapter, Chapter) -> Int = when (manga.sorting) {
Manga.SORTING_SOURCE -> when (sortDescending()) { Manga.SORTING_SOURCE -> when (sortDescending()) {
true -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) } 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) } else -> { c1, c2 -> c1.source_order.compareTo(c2.source_order) }
} }
chapters = chapters.sortedWith(Comparator(sortFunction))
getScrollType(chapters) getScrollType(chapters)
return chapters return chapters.sortedWith(Comparator(sortFunction))
} }
private fun getScrollType(chapters: List<ChapterItem>) { private fun getScrollType(chapters: List<ChapterItem>) {
scrollType = when { scrollType = when {
hasMultipleVolumes(chapters) -> MULTIPLE_VOLUMES ChapterUtil.hasMultipleVolumes(chapters) -> MULTIPLE_VOLUMES
hasMultipleSeasons(chapters) -> MULTIPLE_SEASONS ChapterUtil.hasMultipleSeasons(chapters) -> MULTIPLE_SEASONS
hasTensOfChapters(chapters) -> TENS_OF_CHAPTERS ChapterUtil.hasTensOfChapters(chapters) -> TENS_OF_CHAPTERS
else -> 0 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. * Returns the next unread chapter or null if everything is read.
*/ */
@ -596,10 +519,10 @@ class MangaDetailsPresenter(
fun currentFilters(): String { fun currentFilters(): String {
val filtersId = mutableListOf<Int?>() val filtersId = mutableListOf<Int?>()
filtersId.add(if (onlyRead()) R.string.read else null) filtersId.add(if (manga.readFilter == Manga.SHOW_READ) R.string.read else null)
filtersId.add(if (onlyUnread()) R.string.unread else null) filtersId.add(if (manga.readFilter == Manga.SHOW_UNREAD) R.string.unread else null)
filtersId.add(if (onlyDownloaded()) R.string.downloaded else null) filtersId.add(if (manga.downloadedFilter == Manga.SHOW_DOWNLOADED) R.string.downloaded else null)
filtersId.add(if (onlyBookmarked()) R.string.bookmarked else null) filtersId.add(if (manga.bookmarkedFilter == Manga.SHOW_BOOKMARKED) R.string.bookmarked else null)
return filtersId.filterNotNull().joinToString(", ") { preferences.context.getString(it) } 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.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
import eu.kanade.tachiyomi.ui.reader.model.ViewerChapters 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.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.ImageUtil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -53,11 +54,10 @@ class ReaderPresenter(
private val sourceManager: SourceManager = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(),
private val downloadManager: DownloadManager = Injekt.get(), private val downloadManager: DownloadManager = Injekt.get(),
private val coverCache: CoverCache = 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>() { ) : BasePresenter<ReaderActivity>() {
private val readerChapterFilter = ReaderChapterFilter(downloadManager, preferences)
/** /**
* The manga loaded in the reader. It can be null when instantiated for a short time. * 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") ?: error("Requested chapter of id $chapterId not found in chapter list")
val chaptersForReader = val chaptersForReader =
readerChapterFilter.filterChapter(dbChapters, manga, selectedChapter) chapterFilter.filterChaptersForReader(dbChapters, manga, selectedChapter)
when (manga.sorting) { when (manga.sorting) {
Manga.SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader) Manga.SORTING_SOURCE -> ChapterLoadBySource().get(chaptersForReader)
@ -187,7 +187,7 @@ class ReaderPresenter(
chapterItems = withContext(Dispatchers.IO) { chapterItems = withContext(Dispatchers.IO) {
val dbChapters = db.getChapters(manga).executeAsBlocking() val dbChapters = db.getChapters(manga).executeAsBlocking()
val list = val list =
readerChapterFilter.filterChapter(dbChapters, manga, getCurrentChapter()?.chapter) chapterFilter.filterChaptersForReader(dbChapters, manga, getCurrentChapter()?.chapter)
.sortedBy { .sortedBy {
when (manga.sorting) { when (manga.sorting) {
Manga.SORTING_NUMBER -> it.chapter_number 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.Context
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.widget.TextView import android.widget.TextView
import androidx.core.graphics.ColorUtils
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter 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.contextCompatColor
import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.dpToPxEnd 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 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
}
} }
} }