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:
Seishirou101 2021-09-24 04:49:02 +00:00 committed by GitHub
parent 9bbf96bbef
commit f062baf909
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 138 additions and 11 deletions

View file

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

View file

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

View file

@ -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

View file

@ -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

View file

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

View file

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

View file

@ -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"
}

View file

@ -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

View file

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

View file

@ -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
}
}

View file

@ -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 }
}

View file

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

View file

@ -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" />

View file

@ -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>