Filter chapters by not d/l'd, not bookmarked

Closes #736
This commit is contained in:
Jays2Kings 2021-07-11 03:36:08 -04:00
parent b44e343466
commit c4ebc6b58e
13 changed files with 283 additions and 51 deletions

View File

@ -51,6 +51,7 @@ import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import eu.kanade.tachiyomi.util.system.executeOnIO
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.widget.TriStateCheckBox
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -531,14 +532,26 @@ class MangaDetailsPresenter(
/**
* Removes all filters and requests an UI update.
*/
fun setFilters(read: Boolean, unread: Boolean, downloaded: Boolean, bookmarked: Boolean) {
manga.readFilter = when {
read -> Manga.CHAPTER_SHOW_READ
unread -> Manga.CHAPTER_SHOW_UNREAD
fun setFilters(
unread: TriStateCheckBox.State,
downloaded: TriStateCheckBox.State,
bookmarked: TriStateCheckBox.State
) {
manga.readFilter = when (unread) {
TriStateCheckBox.State.CHECKED -> Manga.CHAPTER_SHOW_UNREAD
TriStateCheckBox.State.INVERSED -> Manga.CHAPTER_SHOW_READ
else -> Manga.SHOW_ALL
}
manga.downloadedFilter = when (downloaded) {
TriStateCheckBox.State.CHECKED -> Manga.CHAPTER_SHOW_DOWNLOADED
TriStateCheckBox.State.INVERSED -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED
else -> Manga.SHOW_ALL
}
manga.bookmarkedFilter = when (bookmarked) {
TriStateCheckBox.State.CHECKED -> Manga.CHAPTER_SHOW_BOOKMARKED
TriStateCheckBox.State.INVERSED -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED
else -> Manga.SHOW_ALL
}
manga.downloadedFilter = if (downloaded) Manga.CHAPTER_SHOW_DOWNLOADED else Manga.SHOW_ALL
manga.bookmarkedFilter = if (bookmarked) Manga.CHAPTER_SHOW_BOOKMARKED else Manga.SHOW_ALL
asyncUpdateMangaAndChapters()
}
@ -555,7 +568,9 @@ class MangaDetailsPresenter(
filtersId.add(if (manga.readFilter == Manga.CHAPTER_SHOW_READ) R.string.read else null)
filtersId.add(if (manga.readFilter == Manga.CHAPTER_SHOW_UNREAD) R.string.unread else null)
filtersId.add(if (manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED) R.string.downloaded else null)
filtersId.add(if (manga.downloadedFilter == Manga.CHAPTER_SHOW_NOT_DOWNLOADED) R.string.not_downloaded else null)
filtersId.add(if (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED) R.string.bookmarked else null)
filtersId.add(if (manga.bookmarkedFilter == Manga.CHAPTER_SHOW_NOT_BOOKMARKED) R.string.not_bookmarked else null)
return filtersId.filterNotNull().joinToString(", ") { preferences.context.getString(it) }
}

View File

@ -2,10 +2,10 @@ package eu.kanade.tachiyomi.ui.manga.chapter
import android.content.Context
import android.util.AttributeSet
import android.widget.CompoundButton
import android.widget.LinearLayout
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.ChapterFilterLayoutBinding
import eu.kanade.tachiyomi.widget.TriStateCheckBox
class ChapterFilterLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
LinearLayout(context, attrs) {
@ -16,38 +16,52 @@ class ChapterFilterLayout @JvmOverloads constructor(context: Context, attrs: Att
super.onFinishInflate()
binding = ChapterFilterLayoutBinding.bind(this)
binding.showAll.setOnCheckedChangeListener(::checkedFilter)
binding.showRead.setOnCheckedChangeListener(::checkedFilter)
binding.showUnread.setOnCheckedChangeListener(::checkedFilter)
binding.showDownload.setOnCheckedChangeListener(::checkedFilter)
binding.showBookmark.setOnCheckedChangeListener(::checkedFilter)
}
private fun checkedFilter(checkBox: CompoundButton, isChecked: Boolean) {
if (isChecked) {
if (binding.showAll == checkBox) {
binding.showRead.isChecked = false
binding.showUnread.isChecked = false
binding.showDownload.isChecked = false
binding.showBookmark.isChecked = false
private fun checkedFilter(checkBox: TriStateCheckBox, state: TriStateCheckBox.State) {
if (state != TriStateCheckBox.State.UNCHECKED) {
if (binding.showAll == checkBox && state == TriStateCheckBox.State.CHECKED) {
binding.showUnread.animateDrawableToState(TriStateCheckBox.State.UNCHECKED)
binding.showDownload.animateDrawableToState(TriStateCheckBox.State.UNCHECKED)
binding.showBookmark.animateDrawableToState(TriStateCheckBox.State.UNCHECKED)
} else {
binding.showAll.isChecked = false
if (binding.showRead == checkBox) binding.showUnread.isChecked = false
else if (binding.showUnread == checkBox) binding.showRead.isChecked = false
if (binding.showAll == checkBox) {
binding.showAll.state = TriStateCheckBox.State.CHECKED
} else {
binding.showAll.animateDrawableToState(TriStateCheckBox.State.UNCHECKED)
}
}
} else if (!binding.showRead.isChecked && !binding.showUnread.isChecked && !binding.showDownload.isChecked && !binding.showBookmark.isChecked) {
binding.showAll.isChecked = true
} else if (
binding.showUnread.isUnchecked &&
binding.showDownload.isUnchecked &&
binding.showBookmark.isUnchecked
) {
binding.showAll.animateDrawableToState(TriStateCheckBox.State.CHECKED)
}
}
fun setCheckboxes(manga: Manga) {
binding.showRead.isChecked = manga.readFilter == Manga.CHAPTER_SHOW_READ
binding.showUnread.isChecked = manga.readFilter == Manga.CHAPTER_SHOW_UNREAD
binding.showDownload.isChecked = manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED
binding.showBookmark.isChecked = manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED
binding.showUnread.state = when (manga.readFilter) {
Manga.CHAPTER_SHOW_UNREAD -> TriStateCheckBox.State.CHECKED
Manga.CHAPTER_SHOW_READ -> TriStateCheckBox.State.INVERSED
else -> TriStateCheckBox.State.UNCHECKED
}
binding.showDownload.state = when (manga.downloadedFilter) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateCheckBox.State.CHECKED
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateCheckBox.State.INVERSED
else -> TriStateCheckBox.State.UNCHECKED
}
binding.showBookmark.state = when (manga.bookmarkedFilter) {
Manga.CHAPTER_SHOW_BOOKMARKED -> TriStateCheckBox.State.CHECKED
Manga.CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateCheckBox.State.INVERSED
else -> TriStateCheckBox.State.UNCHECKED
}
binding.showAll.isChecked = !(
binding.showRead.isChecked || binding.showUnread.isChecked ||
binding.showDownload.isChecked || binding.showBookmark.isChecked
)
binding.showAll.isChecked = binding.showUnread.isUnchecked &&
binding.showDownload.isUnchecked &&
binding.showBookmark.isUnchecked
}
}

View File

@ -70,10 +70,9 @@ class ChaptersSortBottomSheet(controller: MangaDetailsController) :
setOnDismissListener {
presenter.setFilters(
binding.chapterFilterLayout.showRead.isChecked,
binding.chapterFilterLayout.showUnread.isChecked,
binding.chapterFilterLayout.showDownload.isChecked,
binding.chapterFilterLayout.showBookmark.isChecked
binding.chapterFilterLayout.showUnread.state,
binding.chapterFilterLayout.showDownload.state,
binding.chapterFilterLayout.showBookmark.state
)
}
}

View File

@ -14,15 +14,19 @@ class ChapterFilter(val preferences: PreferencesHelper = Injekt.get(), val downl
val readEnabled = manga.readFilter == Manga.CHAPTER_SHOW_READ
val unreadEnabled = manga.readFilter == Manga.CHAPTER_SHOW_UNREAD
val downloadEnabled = manga.downloadedFilter == Manga.CHAPTER_SHOW_DOWNLOADED
val notDownloadEnabled = manga.downloadedFilter == Manga.CHAPTER_SHOW_NOT_DOWNLOADED
val bookmarkEnabled = manga.bookmarkedFilter == Manga.CHAPTER_SHOW_BOOKMARKED
val notBookmarkEnabled = manga.bookmarkedFilter == Manga.CHAPTER_SHOW_NOT_BOOKMARKED
// if none of the filters are enabled skip the filtering of them
return if (readEnabled || unreadEnabled || downloadEnabled || bookmarkEnabled) {
return if (readEnabled || unreadEnabled || downloadEnabled || notDownloadEnabled || bookmarkEnabled || notBookmarkEnabled) {
chapters.filter {
if (readEnabled && it.read.not() ||
(unreadEnabled && it.read) ||
(bookmarkEnabled && it.bookmark.not()) ||
(downloadEnabled && downloadManager.isChapterDownloaded(it, manga).not())
(notBookmarkEnabled && it.bookmark) ||
(downloadEnabled && downloadManager.isChapterDownloaded(it, manga).not()) ||
(notDownloadEnabled && downloadManager.isChapterDownloaded(it, manga))
) {
return@filter false
}

View File

@ -0,0 +1,172 @@
package eu.kanade.tachiyomi.widget
import android.content.Context
import android.content.res.ColorStateList
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.TriStateCheckBoxBinding
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setAnimVectorCompat
import eu.kanade.tachiyomi.util.view.setVectorCompat
class TriStateCheckBox constructor(context: Context, attrs: AttributeSet?) :
FrameLayout(context, attrs) {
var text: CharSequence
get() {
return binding.textView.text
}
set(value) {
binding.textView.text = value
}
var state: State = State.UNCHECKED
set(value) {
field = value
updateDrawable()
}
var isUnchecked: Boolean
get() = state == State.UNCHECKED
set(value) {
state = if (value) State.UNCHECKED else State.CHECKED
}
var isChecked: Boolean
get() = state == State.UNCHECKED
set(value) {
state = if (value) State.CHECKED else State.UNCHECKED
}
private val binding = TriStateCheckBoxBinding.inflate(
LayoutInflater.from(context),
this,
false
)
private var mOnCheckedChangeListener: OnCheckedChangeListener? = null
init {
addView(binding.root)
val a = context.obtainStyledAttributes(attrs, R.styleable.TriStateCheckBox, 0, 0)
val str = a.getString(R.styleable.TriStateCheckBox_android_text) ?: ""
text = str
val maxLines = a.getInt(R.styleable.TriStateCheckBox_android_maxLines, Int.MAX_VALUE)
binding.textView.maxLines = maxLines
a.recycle()
setOnClickListener {
setState(
when (state) {
State.CHECKED -> State.INVERSED
State.UNCHECKED -> State.CHECKED
else -> State.UNCHECKED
},
true
)
mOnCheckedChangeListener?.onCheckedChanged(this, state)
}
}
fun setState(state: State, animated: Boolean = false) {
if (animated) {
animateDrawableToState(state)
} else {
this.state = state
}
}
/**
* Register a callback to be invoked when the checked state of this button
* changes.
*
* @param listener the callback to call on checked state change
*/
fun setOnCheckedChangeListener(listener: OnCheckedChangeListener?) {
mOnCheckedChangeListener = listener
}
fun animateDrawableToState(state: State) {
val oldState = this.state
if (state == oldState) return
this.state = state
with(binding.triStateBox) {
when (state) {
State.UNCHECKED -> {
setAnimVectorCompat(
when (oldState) {
State.INVERSED -> R.drawable.anim_check_box_x_to_blank_24dp
else -> R.drawable.anim_checkbox_checked_to_blank_24dp
},
R.attr.colorControlNormal
)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorControlNormal))
}
State.CHECKED -> {
setAnimVectorCompat(
R.drawable.anim_check_box_blank_to_checked_24dp,
R.attr.colorAccent
)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorAccent))
}
State.INVERSED -> {
setAnimVectorCompat(
R.drawable.anim_check_box_checked_to_x_24dp,
R.attr.colorAccentText
)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorAccentText))
}
}
}
}
fun updateDrawable() {
with(binding.triStateBox) {
when (state) {
State.UNCHECKED -> {
setVectorCompat(
R.drawable.ic_check_box_outline_blank_24dp,
R.attr.colorControlNormal
)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorControlNormal))
}
State.CHECKED -> {
setVectorCompat(R.drawable.ic_check_box_24dp, R.attr.colorAccent)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorAccent))
}
State.INVERSED -> {
setVectorCompat(
R.drawable.ic_check_box_x_24dp,
R.attr.colorAccentText
)
backgroundTintList = ColorStateList.valueOf(context.getResourceColor(R.attr.colorAccentText))
}
}
}
}
enum class State {
UNCHECKED,
CHECKED,
INVERSED,
;
}
/**
* Interface definition for a callback to be invoked when the checked state
* of a compound button changed.
*/
fun interface OnCheckedChangeListener {
/**
* Called when the checked state of a compound button has changed.
*
* @param buttonView The compound button view whose state has changed.
* @param state The new checked state of buttonView.
*/
fun onCheckedChanged(buttonView: TriStateCheckBox, state: State)
}
}

View File

@ -13,6 +13,7 @@ class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: Attri
var state: State = State.UNCHECKED
fun animateDrawableToState(state: State) {
if (state == this.state) return
when (state) {
State.UNCHECKED -> setAnimVectorCompat(
when (this.state) {

View File

@ -398,7 +398,6 @@
android:padding="5dp"
android:textAlignment="textEnd"
android:textColor="?android:textColorHint"
app:layout_constraintBaseline_toBaselineOf="@id/chapters_title"
app:layout_constraintBottom_toBottomOf="@id/filter_button"
app:layout_constraintEnd_toStartOf="@id/filter_button"
app:layout_constraintStart_toEndOf="@+id/chapters_title"

View File

@ -398,7 +398,6 @@
android:padding="5dp"
android:textAlignment="textEnd"
android:textColor="?android:textColorHint"
app:layout_constraintBaseline_toBaselineOf="@id/chapters_title"
app:layout_constraintBottom_toBottomOf="@id/filter_button"
app:layout_constraintEnd_toStartOf="@id/filter_button"
app:layout_constraintStart_toEndOf="@+id/chapters_title"

View File

@ -2,6 +2,7 @@
<eu.kanade.tachiyomi.ui.manga.chapter.ChapterFilterLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
@ -13,7 +14,7 @@
android:paddingEnd="12dp"
android:text="@string/filter" />
<com.google.android.material.checkbox.MaterialCheckBox
<eu.kanade.tachiyomi.widget.TriStateCheckBox
android:id="@+id/show_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -21,15 +22,7 @@
android:layout_marginEnd="12dp"
android:text="@string/show_all" />
<com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/show_read"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="@string/show_read_chapters" />
<com.google.android.material.checkbox.MaterialCheckBox
<eu.kanade.tachiyomi.widget.TriStateCheckBox
android:id="@+id/show_unread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -37,15 +30,15 @@
android:layout_marginEnd="12dp"
android:text="@string/show_unread_chapters" />
<com.google.android.material.checkbox.MaterialCheckBox
<eu.kanade.tachiyomi.widget.TriStateCheckBox
android:id="@+id/show_download"
android:text="@string/show_downloaded_chapters"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginEnd="12dp"
android:text="@string/show_downloaded_chapters" />
android:layout_marginEnd="12dp" />
<com.google.android.material.checkbox.MaterialCheckBox
<eu.kanade.tachiyomi.widget.TriStateCheckBox
android:id="@+id/show_bookmark"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -398,7 +398,6 @@
android:padding="5dp"
android:textAlignment="textEnd"
android:textColor="?android:textColorHint"
app:layout_constraintBaseline_toBaselineOf="@id/chapters_title"
app:layout_constraintBottom_toBottomOf="@id/filter_button"
app:layout_constraintEnd_toStartOf="@id/filter_button"
app:layout_constraintStart_toEndOf="@+id/chapters_title"

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/tri_state_box"
android:contentDescription="@string/enable"
android:padding="4dp"
app:srcCompat="@drawable/ic_check_box_outline_blank_24dp"
tools:tint="?colorControlNormal"
android:background="@drawable/round_ripple"
style="@style/MD_ListItem_Control" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_view"
android:layout_marginStart="2dp"
android:layout_marginEnd="4dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textSize="14sp"
style="@style/TextAppearance.Regular"
tools:text="Item" />
</LinearLayout>

View File

@ -26,6 +26,11 @@
<attr name="summary" format="reference|string" />
</declare-styleable>
<declare-styleable name="TriStateCheckBox">
<attr name="android:text" format="reference|string"/>
<attr name="android:maxLines"/>
</declare-styleable>
<declare-styleable name="MenuSheetItemView">
<attr name="android:text"/>
<attr name="android:maxLines"/>

View File

@ -49,6 +49,7 @@
<string name="chapter_x_of_y">Chapter %1$d of %2$d</string>
<string name="all_chapters_read">All chapters read</string>
<string name="bookmarked">Bookmarked</string>
<string name="not_bookmarked">Not bookmarked</string>
<string name="marked_as_read">Marked as read</string>
<string name="marked_as_unread">Marked as unread</string>
<string name="removed_bookmark">Removed bookmark</string>