diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index 0409fc09fb..fe611201d4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -18,9 +18,9 @@ import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet import eu.kanade.tachiyomi.util.lang.removeArticles import eu.kanade.tachiyomi.util.system.executeOnIO -import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE -import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE -import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE +import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_EXCLUDE +import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_IGNORE +import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_INCLUDE import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt index ebcd4ab831..022aacb352 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt @@ -48,7 +48,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri private lateinit var unread: FilterTagGroup - private lateinit var unreadProgress: FilterTagGroup + private lateinit var allUnread: FilterTagGroup private lateinit var completed: FilterTagGroup @@ -188,10 +188,10 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri completed.setup(this, R.string.completed, R.string.ongoing) unread = inflate(R.layout.filter_buttons) as FilterTagGroup - unread.setup(this, R.string.unread, R.string.read) + unread.setup(this, R.string.not_started, R.string.in_progress, R.string.read) - unreadProgress = inflate(R.layout.filter_buttons) as FilterTagGroup - unreadProgress.setup(this, R.string.not_started, R.string.in_progress) + allUnread = inflate(R.layout.filter_buttons) as FilterTagGroup + allUnread.setup(this, R.string.all_unread) tracked = inflate(R.layout.filter_buttons) as FilterTagGroup tracked.setup(this, R.string.tracked, R.string.not_tracked) @@ -235,14 +235,13 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri hide_categories.visibleIf(showCategoriesCheckBox) downloaded.setState(preferences.filterDownloaded()) completed.setState(preferences.filterCompleted()) - val unreadP = preferences.filterUnread().getOrDefault() - 1 - if (unreadP > 1) { - unread.state = 0 - unreadProgress.state = unreadP - 2 - if (!filterItems.contains(unreadProgress)) - filterItems.add(unreadProgress) - } else { - unread.state = unreadP + val unreadP = preferences.filterUnread().getOrDefault() + if (unreadP == STATE_INCLUDE) { + allUnread.state = 0 + if (!filterItems.contains(allUnread)) + filterItems.add(allUnread) + } else if (unreadP > 0) { + unread.state = if (unreadP in 3..4) unreadP - 3 else 2 } tracked.setState(preferences.filterTracked()) mangaType?.setState(preferences.filterMangaType()) @@ -280,13 +279,16 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri FILTER_TRACKER = view.nameOf(index) ?: "" null } - unreadProgress -> { - preferences.filterUnread().set( - if (index > -1) index + 3 else 1) + unread -> { + preferences.filterUnread().set(if (index in 0..1) index + 3 else 2) + null + } + allUnread -> { + preferences.filterUnread().set(index + 1) + if (index != 0) unread.reset() null } downloaded -> preferences.filterDownloaded() - unread -> preferences.filterUnread() completed -> preferences.filterCompleted() tracked -> preferences.filterTracked() mangaType -> preferences.filterMangaType() @@ -294,14 +296,22 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri }?.set(index + 1) onGroupClicked(ACTION_FILTER) } - if (unread.state == 0 && unreadProgress.parent == null) { + if (allUnread.state == 0 && unread.parent != null) { + filter_layout.removeView(unread) + filterItems.remove(unread) + } else if (allUnread.state != 0 && unread.parent == null) { + filter_layout.addView(unread, 0) + filterItems.add(0, unread) + filter_layout.removeView(allUnread) + filterItems.remove(allUnread) + } else if (unread.state in 0..1 && allUnread.parent == null) { val unreadIndex = filter_layout.indexOfChild(unread) + 1 - filter_layout.addView(unreadProgress, unreadIndex) - filterItems.add(unreadIndex, unreadProgress) - } else if (unread.state != 0 && unreadProgress.parent != null) { - filter_layout.removeView(unreadProgress) - unreadProgress.reset() - filterItems.remove(unreadProgress) + filter_layout.addView(allUnread, unreadIndex) + filterItems.add(unreadIndex, allUnread) + } else if (unread.state != 0 && allUnread.parent != null) { + filter_layout.removeView(allUnread) + allUnread.reset() + filterItems.remove(allUnread) } if (tracked.isActivated && trackers != null && trackers?.parent == null) { filter_layout.addView(trackers) @@ -313,10 +323,11 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri filterItems.remove(trackers!!) } val hasFilters = hasActiveFilters() - if (hasFilters && clearButton.parent == null) + if (hasFilters && clearButton.parent == null) { filter_layout.addView(clearButton, 0) - else if (!hasFilters && clearButton.parent != null) + } else if (!hasFilters && clearButton.parent != null) { filter_layout.removeView(clearButton) + } } private fun clearFilters() { @@ -330,13 +341,16 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri val transition = androidx.transition.AutoTransition() transition.duration = 150 androidx.transition.TransitionManager.beginDelayedTransition(filter_layout, transition) + if (!filterItems.contains(unread)) { + filterItems.add(0, unread) + } filterItems.forEach { it.reset() } trackers?.let { filterItems.remove(it) } - filterItems.remove(unreadProgress) + filterItems.remove(allUnread) reSortViews() onGroupClicked(ACTION_FILTER) } @@ -359,6 +373,11 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri const val ACTION_FILTER = 1 const val ACTION_HIDE_FILTER_TIP = 2 const val ACTION_DISPLAY = 3 + + const val STATE_IGNORE = 0 + const val STATE_INCLUDE = 1 + const val STATE_EXCLUDE = 2 + var FILTER_TRACKER = "" private set } diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt deleted file mode 100644 index e5e5bb9ff2..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt +++ /dev/null @@ -1,267 +0,0 @@ -package eu.kanade.tachiyomi.widget - -import android.content.Context -import android.graphics.drawable.Drawable -import android.util.AttributeSet -import android.view.View -import android.view.ViewGroup -import androidx.annotation.CallSuper -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.RecyclerView -import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.system.getResourceColor - -/** - * An alternative implementation of [com.google.android.material.navigation.NavigationView], without menu - * inflation and allowing customizable items (multiple selections, custom views, etc). - */ -open class ExtendedNavigationView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0 -) : - SimpleNavigationView(context, attrs, defStyleAttr) { - - /** - * Every item of the nav view. Generic items must belong to this list, custom items could be - * implemented by an abstract class. If more customization is needed in the future, this can be - * changed to an interface instead of sealed class. - */ - sealed class Item { - /** - * A view separator. - */ - class Separator(val paddingTop: Int = 0, val paddingBottom: Int = 0) : Item() - - /** - * A header with a title. - */ - class Header(val resTitle: Int) : Item() - - /** - * A checkbox. - */ - open class Checkbox(val resTitle: Int, var checked: Boolean = false) : Item() - - /** - * A checkbox belonging to a group. The group must handle selections and restrictions. - */ - class CheckboxGroup(resTitle: Int, override val group: Group, checked: Boolean = false) : - Checkbox(resTitle, checked), GroupedItem - - /** - * A radio belonging to a group (a sole radio makes no sense). The group must handle - * selections and restrictions. - */ - class Radio(val resTitle: Int, override val group: Group, var checked: Boolean = false) : - Item(), GroupedItem - - /** - * An item with which needs more than two states (selected/deselected). - */ - abstract class MultiState(val resTitle: Int, var state: Int = 0) : Item() { - - /** - * Returns the drawable associated to every possible each state. - */ - abstract fun getStateDrawable(context: Context): Drawable? - - /** - * Creates a vector tinted with the accent color. - * - * @param context any context. - * @param resId the vector resource to load and tint - */ - fun tintVector(context: Context, resId: Int): Drawable { - return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { - setTint(context.getResourceColor(R.attr.colorAccent)) - } - } - - /** - * Creates a vector tinted with the accent color. - * - * @param context any context. - * @param resId the vector resource to load and tint - */ - fun tintVector(context: Context, resId: Int, colorId: Int): Drawable { - return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply { - setTint(context.getResourceColor(colorId)) - } - } - } - - /** - * An item with which needs more than two states (selected/deselected) belonging to a group. - * The group must handle selections and restrictions. - */ - abstract class MultiStateGroup(resTitle: Int, override val group: Group, state: Int = 0) : - MultiState(resTitle, state), GroupedItem - - /** - * A multistate item for sorting lists (unselected, ascending, descending). - */ - class MultiSort(resId: Int, group: Group) : MultiStateGroup(resId, group) { - - companion object { - const val SORT_NONE = 0 - const val SORT_ASC = 1 - const val SORT_DESC = 2 - } - - override fun getStateDrawable(context: Context): Drawable? { - return when (state) { - SORT_ASC -> tintVector(context, R.drawable.ic_arrow_up_white_32dp) - SORT_DESC -> tintVector(context, R.drawable.ic_arrow_down_white_32dp) - SORT_NONE -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp) - else -> null - } - } - } - - class TriStateGroup(resId: Int, group: Group) : MultiStateGroup(resId, group) { - - companion object { - const val STATE_IGNORE = 0 - const val STATE_INCLUDE = 1 - const val STATE_EXCLUDE = 2 - const val STATE_REALLY_EXCLUDE = 3 - } - - override fun getStateDrawable(context: Context): Drawable? { - return when (state) { - STATE_INCLUDE -> tintVector(context, R.drawable.ic_check_box_24dp) - STATE_EXCLUDE -> tintVector(context, R.drawable.ic_check_box_x_24dp, - android.R.attr.textColorSecondary) - else -> tintVector(context, R.drawable.ic_check_box_outline_blank_24dp, - android.R.attr.textColorSecondary) - } - } - } - } - - /** - * Interface for an item belonging to a group. - */ - interface GroupedItem { - val group: Group - } - - /** - * A group containing a list of items. - */ - interface Group { - - /** - * An optional header for the group, typically a [Item.Header]. - */ - val header: Item? - - /** - * An optional footer for the group, typically a [Item.Separator]. - */ - val footer: Item? - - /** - * The items of the group, excluding header and footer. - */ - val items: List - - /** - * Creates all the elements of this group. Implementations can override this method for more - * customization. - */ - fun createItems() = (mutableListOf() + header + items + footer).filterNotNull() - - /** - * Called after creating the list of items. Implementations should load the current values - * into the models. - */ - fun initModels() - - /** - * Called when an item of this group is clicked. The group is responsible for all the - * selections of its items. - */ - fun onItemClicked(item: Item) - } - - /** - * Base adapter for the navigation view. It knows how to create and render every subclass of - * [Item]. - */ - abstract inner class Adapter(private val items: List) : RecyclerView.Adapter() { - - private val onClick = View.OnClickListener { - val pos = recycler.getChildAdapterPosition(it) - val item = items[pos] - onItemClicked(item) - } - - fun notifyItemChanged(item: Item) { - val pos = items.indexOf(item) - if (pos != -1) notifyItemChanged(pos) - } - - override fun getItemCount(): Int { - return items.size - } - - @CallSuper - override fun getItemViewType(position: Int): Int { - return when (items[position]) { - is Item.Header -> VIEW_TYPE_HEADER - is Item.Separator -> VIEW_TYPE_SEPARATOR - is Item.Radio -> VIEW_TYPE_RADIO - is Item.Checkbox -> VIEW_TYPE_CHECKBOX - is Item.MultiState -> VIEW_TYPE_MULTISTATE - } - } - - @CallSuper - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { - return when (viewType) { - VIEW_TYPE_HEADER -> HeaderHolder(parent) - VIEW_TYPE_SEPARATOR -> SeparatorHolder(parent) - VIEW_TYPE_RADIO -> RadioHolder(parent, onClick) - VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, onClick) - VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, onClick) - else -> throw Exception("Unknown view type") - } - } - - @CallSuper - override fun onBindViewHolder(holder: Holder, position: Int) { - when (holder) { - is HeaderHolder -> { - val item = items[position] as Item.Header - holder.title.setText(item.resTitle) - } - is SeparatorHolder -> { - val view = holder.itemView - val item = items[position] as Item.Separator - view.setPadding(0, item.paddingTop, 0, item.paddingBottom) - } - is RadioHolder -> { - val item = items[position] as Item.Radio - holder.radio.setText(item.resTitle) - holder.radio.isChecked = item.checked - } - is CheckboxHolder -> { - val item = items[position] as Item.CheckboxGroup - holder.check.setText(item.resTitle) - holder.check.isChecked = item.checked - } - is MultiStateHolder -> { - val item = items[position] as Item.MultiStateGroup - val drawable = item.getStateDrawable(context) - holder.text.setText(item.resTitle) - holder.text.setCompoundDrawablesWithIntrinsicBounds(drawable, null, null, null) - } - } - } - - abstract fun onItemClicked(item: Item) - } -} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e39b9c392f..68652d5a1a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -115,6 +115,7 @@ library from the browse tab. No matches found for your current filters Show all categories + All unread Sort by: %1$s