mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 10:44:42 +00:00
add scanlation filter (#1006)
* add scanlation filter * add hide logic and remove sql cmd * add suggested fixes * use button colored instead * move filter scanlation code to getChaptersSorted * invert scanlation filter and remove strings * fix ChapterSort issue and add filter logic to getNextUnreadChapter * remove log import * filter chapterlist in reader presenter * make new method and replace logic * add missed comma * add suggested improvements * add missed suggestion
This commit is contained in:
parent
9bbf96bbef
commit
f062baf909
14 changed files with 138 additions and 11 deletions
|
@ -20,7 +20,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = 13
|
||||
const val DATABASE_VERSION = 14
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
|
@ -87,6 +87,9 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
|||
db.execSQL(TrackTable.addStartDate)
|
||||
db.execSQL(TrackTable.addFinishDate)
|
||||
}
|
||||
if (oldVersion < 14) {
|
||||
db.execSQL(MangaTable.addFilteredScanlators)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_CHAPTER_FLAGS
|
|||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DATE_ADDED
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_DESCRIPTION
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FAVORITE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_FILTERED_SCANLATORS
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_GENRE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_HIDE_TITLE
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable.COL_ID
|
||||
|
@ -66,6 +67,7 @@ class MangaPutResolver : DefaultPutResolver<Manga>() {
|
|||
put(COL_HIDE_TITLE, obj.hide_title)
|
||||
put(COL_CHAPTER_FLAGS, obj.chapter_flags)
|
||||
put(COL_DATE_ADDED, obj.date_added)
|
||||
put(COL_FILTERED_SCANLATORS, obj.filtered_scanlators)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,6 +90,7 @@ interface BaseMangaGetResolver {
|
|||
chapter_flags = cursor.getInt(cursor.getColumnIndex(COL_CHAPTER_FLAGS))
|
||||
hide_title = cursor.getInt(cursor.getColumnIndex(COL_HIDE_TITLE)) == 1
|
||||
date_added = cursor.getLong(cursor.getColumnIndex(COL_DATE_ADDED))
|
||||
filtered_scanlators = cursor.getString(cursor.getColumnIndex(COL_FILTERED_SCANLATORS))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ interface Manga : SManga {
|
|||
|
||||
var hide_title: Boolean
|
||||
|
||||
var filtered_scanlators: String?
|
||||
|
||||
fun isBlank() = id == Long.MIN_VALUE
|
||||
fun isHidden() = status == -1
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ open class MangaImpl : Manga {
|
|||
|
||||
override var date_added: Long = 0
|
||||
|
||||
override var filtered_scanlators: String? = null
|
||||
|
||||
lateinit var ogTitle: String
|
||||
private set
|
||||
var ogAuthor: String? = null
|
||||
|
|
|
@ -9,6 +9,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
|||
import eu.kanade.tachiyomi.data.database.resolvers.LibraryMangaGetResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaDateAddedPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFavoritePutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFilteredScanlatorsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaFlagsPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaInfoPutResolver
|
||||
import eu.kanade.tachiyomi.data.database.resolvers.MangaLastUpdatedPutResolver
|
||||
|
@ -167,4 +168,9 @@ interface MangaQueries : DbProvider {
|
|||
|
||||
fun getTotalChapterManga() = db.get().listOfObjects(Manga::class.java)
|
||||
.withQuery(RawQuery.builder().query(getTotalChapterMangaQuery()).observesTables(MangaTable.TABLE).build()).prepare()
|
||||
|
||||
fun updateMangaFilteredScanlators(manga: Manga) = db.put()
|
||||
.`object`(manga)
|
||||
.withPutResolver(MangaFilteredScanlatorsPutResolver())
|
||||
.prepare()
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package eu.kanade.tachiyomi.data.database.resolvers
|
||||
|
||||
import androidx.core.content.contentValuesOf
|
||||
import com.pushtorefresh.storio.sqlite.StorIOSQLite
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResolver
|
||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult
|
||||
import com.pushtorefresh.storio.sqlite.queries.UpdateQuery
|
||||
import eu.kanade.tachiyomi.data.database.inTransactionReturn
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.tables.MangaTable
|
||||
|
||||
class MangaFilteredScanlatorsPutResolver : PutResolver<Manga>() {
|
||||
|
||||
override fun performPut(db: StorIOSQLite, manga: Manga) = db.inTransactionReturn {
|
||||
val updateQuery = mapToUpdateQuery(manga)
|
||||
val contentValues = mapToContentValues(manga)
|
||||
|
||||
val numberOfRowsUpdated = db.lowLevel().update(updateQuery, contentValues)
|
||||
PutResult.newUpdateResult(numberOfRowsUpdated, updateQuery.table())
|
||||
}
|
||||
|
||||
fun mapToUpdateQuery(manga: Manga) = UpdateQuery.builder()
|
||||
.table(MangaTable.TABLE)
|
||||
.where("${MangaTable.COL_ID} = ?")
|
||||
.whereArgs(manga.id)
|
||||
.build()
|
||||
|
||||
fun mapToContentValues(manga: Manga) = contentValuesOf(
|
||||
MangaTable.COL_FILTERED_SCANLATORS to manga.filtered_scanlators
|
||||
)
|
||||
}
|
|
@ -44,6 +44,8 @@ object MangaTable {
|
|||
|
||||
const val COL_DATE_ADDED = "date_added"
|
||||
|
||||
const val COL_FILTERED_SCANLATORS = "filtered_scanlators"
|
||||
|
||||
val createTableQuery: String
|
||||
get() =
|
||||
"""CREATE TABLE $TABLE(
|
||||
|
@ -63,7 +65,8 @@ object MangaTable {
|
|||
$COL_VIEWER INTEGER NOT NULL,
|
||||
$COL_HIDE_TITLE INTEGER NOT NULL,
|
||||
$COL_CHAPTER_FLAGS INTEGER NOT NULL,
|
||||
$COL_DATE_ADDED LONG
|
||||
$COL_DATE_ADDED LONG,
|
||||
$COL_FILTERED_SCANLATORS TEXT
|
||||
|
||||
)"""
|
||||
|
||||
|
@ -79,4 +82,7 @@ object MangaTable {
|
|||
|
||||
val addDateAddedCol: String
|
||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_DATE_ADDED LONG DEFAULT 0"
|
||||
|
||||
val addFilteredScanlators: String
|
||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_FILTERED_SCANLATORS TEXT"
|
||||
}
|
||||
|
|
|
@ -109,7 +109,7 @@ class MangaDetailsPresenter(
|
|||
|
||||
var headerItem = MangaHeaderItem(manga, controller.fromCatalogue)
|
||||
var tabletChapterHeaderItem: MangaHeaderItem? = null
|
||||
|
||||
var allChapterScanlators: Set<String> = emptySet()
|
||||
fun onCreate() {
|
||||
headerItem.isTablet = controller.isTablet
|
||||
if (controller.isTablet) {
|
||||
|
@ -158,7 +158,7 @@ class MangaDetailsPresenter(
|
|||
|
||||
// Find downloaded chapters
|
||||
setDownloadedChapters(chapters)
|
||||
|
||||
allChapterScanlators = chapters.flatMap { ChapterUtil.getScanlators(it.chapter.scanlator) }.toSet()
|
||||
// Store the last emission
|
||||
allChapters = chapters
|
||||
this.chapters = applyChapterFilters(chapters)
|
||||
|
@ -629,6 +629,13 @@ class MangaDetailsPresenter(
|
|||
return filtersId.filterNotNull().joinToString(", ") { preferences.context.getString(it) }
|
||||
}
|
||||
|
||||
fun setScanlatorFilter(filteredScanlators: Set<String>) {
|
||||
val manga = manga
|
||||
manga.filtered_scanlators = if (filteredScanlators.size == allChapterScanlators.size || filteredScanlators.isEmpty()) null else ChapterUtil.getScanlatorString(filteredScanlators)
|
||||
db.updateMangaFilteredScanlators(manga).executeAsBlocking()
|
||||
asyncUpdateMangaAndChapters()
|
||||
}
|
||||
|
||||
fun toggleFavorite(): Boolean {
|
||||
manga.favorite = !manga.favorite
|
||||
|
||||
|
|
|
@ -4,10 +4,15 @@ import android.os.Bundle
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.databinding.ChapterSortBottomSheetBinding
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.util.chapter.ChapterUtil
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.view.setBottomEdge
|
||||
import eu.kanade.tachiyomi.widget.E2EBottomSheetDialog
|
||||
|
@ -148,6 +153,33 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) :
|
|||
binding.chapterFilterLayout.setAsDefaultFilter.isInvisible = true
|
||||
binding.chapterFilterLayout.resetAsDefaultFilter.isInvisible = true
|
||||
}
|
||||
binding.filterGroupsButton.isVisible = presenter.allChapterScanlators.isNotEmpty()
|
||||
|
||||
binding.filterGroupsButton.setOnClickListener {
|
||||
val scanlators = presenter.allChapterScanlators.toList()
|
||||
val filteredScanlators =
|
||||
presenter.manga.filtered_scanlators?.let { ChapterUtil.getScanlators(it) }
|
||||
?: scanlators.toSet()
|
||||
val preselected = if (scanlators.size == filteredScanlators.size) {
|
||||
IntArray(0)
|
||||
} else {
|
||||
filteredScanlators.map { scanlators.indexOf(it) }.toIntArray()
|
||||
}
|
||||
MaterialDialog(activity!!)
|
||||
.listItemsMultiChoice(
|
||||
items = scanlators,
|
||||
initialSelection = preselected,
|
||||
allowEmptySelection = true,
|
||||
) { _, selections, _ ->
|
||||
val selected = selections.map { scanlators[it] }.toSet()
|
||||
presenter.setScanlatorFilter(selected)
|
||||
}
|
||||
.negativeButton(R.string.reset) {
|
||||
presenter.setScanlatorFilter(presenter.allChapterScanlators)
|
||||
}
|
||||
.positiveButton(R.string.filter)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setFilters(filterLayout: ChapterFilterLayout) {
|
||||
|
|
|
@ -19,8 +19,9 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
|
|||
val notBookmarkEnabled = manga.bookmarkedFilter(preferences) == Manga.CHAPTER_SHOW_NOT_BOOKMARKED
|
||||
|
||||
// if none of the filters are enabled skip the filtering of them
|
||||
val filteredChapters = filterChaptersByScanlators(chapters, manga)
|
||||
return if (readEnabled || unreadEnabled || downloadEnabled || notDownloadEnabled || bookmarkEnabled || notBookmarkEnabled) {
|
||||
chapters.filter {
|
||||
filteredChapters.filter {
|
||||
if (readEnabled && it.read.not() ||
|
||||
(unreadEnabled && it.read) ||
|
||||
(bookmarkEnabled && it.bookmark.not()) ||
|
||||
|
@ -33,25 +34,24 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
|
|||
return@filter true
|
||||
}
|
||||
} else {
|
||||
chapters
|
||||
filteredChapters
|
||||
}
|
||||
}
|
||||
|
||||
// filter chapters for the reader
|
||||
fun <T : Chapter> filterChaptersForReader(chapters: List<T>, manga: Manga, selectedChapter: T? = null): List<T> {
|
||||
var filteredChapters = filterChaptersByScanlators(chapters, manga)
|
||||
// if neither preference is enabled don't even filter
|
||||
if (!preferences.skipRead() && !preferences.skipFiltered()) {
|
||||
return chapters
|
||||
return filteredChapters
|
||||
}
|
||||
|
||||
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 }
|
||||
|
@ -61,6 +61,15 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
|
|||
filteredChapters = mutableList.toList()
|
||||
}
|
||||
}
|
||||
|
||||
return filteredChapters
|
||||
}
|
||||
// filters chapters for scanlators
|
||||
|
||||
fun <T : Chapter> filterChaptersByScanlators(chapters: List<T>, manga: Manga): List<T> {
|
||||
return manga.filtered_scanlators?.let { filteredScanlatorString ->
|
||||
val filteredScanlators = ChapterUtil.getScanlators(filteredScanlatorString)
|
||||
chapters.filter { ChapterUtil.getScanlators(it.scanlator).none { group -> filteredScanlators.contains(group) } }
|
||||
} ?: chapters
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,8 +30,9 @@ class ChapterSort(val manga: Manga, val chapterFilter: ChapterFilter = Injekt.ge
|
|||
fun <T : Chapter> getNextUnreadChapter(rawChapters: List<T>, andFiltered: Boolean = true,): T? {
|
||||
val chapters = when {
|
||||
andFiltered -> chapterFilter.filterChapters(rawChapters, manga)
|
||||
else -> rawChapters
|
||||
else -> chapterFilter.filterChaptersByScanlators(rawChapters, manga)
|
||||
}
|
||||
|
||||
return chapters.sortedWith(sortComparator(true)).find { !it.read }
|
||||
}
|
||||
|
||||
|
|
|
@ -140,5 +140,16 @@ class ChapterUtil {
|
|||
fun hasTensOfChapters(chapters: List<ChapterItem>): Boolean {
|
||||
return chapters.size > 20
|
||||
}
|
||||
|
||||
private const val scanlatorSeparator = " & "
|
||||
|
||||
fun getScanlators(scanlators: String?): List<String> {
|
||||
if (scanlators.isNullOrBlank()) return emptyList()
|
||||
return scanlators.split(scanlatorSeparator).distinct()
|
||||
}
|
||||
|
||||
fun getScanlatorString(scanlators: Set<String>): String {
|
||||
return scanlators.toList().sorted().joinToString(scanlatorSeparator)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,12 +88,25 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/filter_groups_button"
|
||||
style="@style/Theme.Widget.Button.Colored"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:text="@string/filter_groups"
|
||||
android:textAlignment="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/hide_titles"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:text="@string/hide_chapter_titles" />
|
||||
|
||||
|
|
|
@ -531,6 +531,7 @@
|
|||
<string name="error_sharing_cover">Error sharing cover</string>
|
||||
<string name="custom_manga_info">Custom manga info</string>
|
||||
<string name="set_as_default">Set as default</string>
|
||||
<string name="filter_groups">Filter scanlator groups</string>
|
||||
<plurals name="deleted_chapters">
|
||||
<item quantity="one">A chapter has been removed from the source:\n%2$s\nDelete
|
||||
its download?</item>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue