mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-10 01:55:06 +01:00
Reversed ordering of unread filters
part 1 of the ducc complaints Also cleaned out extendednavview
This commit is contained in:
parent
3c56dc6cba
commit
b8f9de1571
@ -18,9 +18,9 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
|||||||
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet
|
||||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_EXCLUDE
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_IGNORE
|
||||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_INCLUDE
|
import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_INCLUDE
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
@ -48,7 +48,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
|
|
||||||
private lateinit var unread: FilterTagGroup
|
private lateinit var unread: FilterTagGroup
|
||||||
|
|
||||||
private lateinit var unreadProgress: FilterTagGroup
|
private lateinit var allUnread: FilterTagGroup
|
||||||
|
|
||||||
private lateinit var completed: 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)
|
completed.setup(this, R.string.completed, R.string.ongoing)
|
||||||
|
|
||||||
unread = inflate(R.layout.filter_buttons) as FilterTagGroup
|
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
|
allUnread = inflate(R.layout.filter_buttons) as FilterTagGroup
|
||||||
unreadProgress.setup(this, R.string.not_started, R.string.in_progress)
|
allUnread.setup(this, R.string.all_unread)
|
||||||
|
|
||||||
tracked = inflate(R.layout.filter_buttons) as FilterTagGroup
|
tracked = inflate(R.layout.filter_buttons) as FilterTagGroup
|
||||||
tracked.setup(this, R.string.tracked, R.string.not_tracked)
|
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)
|
hide_categories.visibleIf(showCategoriesCheckBox)
|
||||||
downloaded.setState(preferences.filterDownloaded())
|
downloaded.setState(preferences.filterDownloaded())
|
||||||
completed.setState(preferences.filterCompleted())
|
completed.setState(preferences.filterCompleted())
|
||||||
val unreadP = preferences.filterUnread().getOrDefault() - 1
|
val unreadP = preferences.filterUnread().getOrDefault()
|
||||||
if (unreadP > 1) {
|
if (unreadP == STATE_INCLUDE) {
|
||||||
unread.state = 0
|
allUnread.state = 0
|
||||||
unreadProgress.state = unreadP - 2
|
if (!filterItems.contains(allUnread))
|
||||||
if (!filterItems.contains(unreadProgress))
|
filterItems.add(allUnread)
|
||||||
filterItems.add(unreadProgress)
|
} else if (unreadP > 0) {
|
||||||
} else {
|
unread.state = if (unreadP in 3..4) unreadP - 3 else 2
|
||||||
unread.state = unreadP
|
|
||||||
}
|
}
|
||||||
tracked.setState(preferences.filterTracked())
|
tracked.setState(preferences.filterTracked())
|
||||||
mangaType?.setState(preferences.filterMangaType())
|
mangaType?.setState(preferences.filterMangaType())
|
||||||
@ -280,13 +279,16 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
FILTER_TRACKER = view.nameOf(index) ?: ""
|
FILTER_TRACKER = view.nameOf(index) ?: ""
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
unreadProgress -> {
|
unread -> {
|
||||||
preferences.filterUnread().set(
|
preferences.filterUnread().set(if (index in 0..1) index + 3 else 2)
|
||||||
if (index > -1) index + 3 else 1)
|
null
|
||||||
|
}
|
||||||
|
allUnread -> {
|
||||||
|
preferences.filterUnread().set(index + 1)
|
||||||
|
if (index != 0) unread.reset()
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
downloaded -> preferences.filterDownloaded()
|
downloaded -> preferences.filterDownloaded()
|
||||||
unread -> preferences.filterUnread()
|
|
||||||
completed -> preferences.filterCompleted()
|
completed -> preferences.filterCompleted()
|
||||||
tracked -> preferences.filterTracked()
|
tracked -> preferences.filterTracked()
|
||||||
mangaType -> preferences.filterMangaType()
|
mangaType -> preferences.filterMangaType()
|
||||||
@ -294,14 +296,22 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
}?.set(index + 1)
|
}?.set(index + 1)
|
||||||
onGroupClicked(ACTION_FILTER)
|
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
|
val unreadIndex = filter_layout.indexOfChild(unread) + 1
|
||||||
filter_layout.addView(unreadProgress, unreadIndex)
|
filter_layout.addView(allUnread, unreadIndex)
|
||||||
filterItems.add(unreadIndex, unreadProgress)
|
filterItems.add(unreadIndex, allUnread)
|
||||||
} else if (unread.state != 0 && unreadProgress.parent != null) {
|
} else if (unread.state != 0 && allUnread.parent != null) {
|
||||||
filter_layout.removeView(unreadProgress)
|
filter_layout.removeView(allUnread)
|
||||||
unreadProgress.reset()
|
allUnread.reset()
|
||||||
filterItems.remove(unreadProgress)
|
filterItems.remove(allUnread)
|
||||||
}
|
}
|
||||||
if (tracked.isActivated && trackers != null && trackers?.parent == null) {
|
if (tracked.isActivated && trackers != null && trackers?.parent == null) {
|
||||||
filter_layout.addView(trackers)
|
filter_layout.addView(trackers)
|
||||||
@ -313,10 +323,11 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
filterItems.remove(trackers!!)
|
filterItems.remove(trackers!!)
|
||||||
}
|
}
|
||||||
val hasFilters = hasActiveFilters()
|
val hasFilters = hasActiveFilters()
|
||||||
if (hasFilters && clearButton.parent == null)
|
if (hasFilters && clearButton.parent == null) {
|
||||||
filter_layout.addView(clearButton, 0)
|
filter_layout.addView(clearButton, 0)
|
||||||
else if (!hasFilters && clearButton.parent != null)
|
} else if (!hasFilters && clearButton.parent != null) {
|
||||||
filter_layout.removeView(clearButton)
|
filter_layout.removeView(clearButton)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun clearFilters() {
|
private fun clearFilters() {
|
||||||
@ -330,13 +341,16 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
val transition = androidx.transition.AutoTransition()
|
val transition = androidx.transition.AutoTransition()
|
||||||
transition.duration = 150
|
transition.duration = 150
|
||||||
androidx.transition.TransitionManager.beginDelayedTransition(filter_layout, transition)
|
androidx.transition.TransitionManager.beginDelayedTransition(filter_layout, transition)
|
||||||
|
if (!filterItems.contains(unread)) {
|
||||||
|
filterItems.add(0, unread)
|
||||||
|
}
|
||||||
filterItems.forEach {
|
filterItems.forEach {
|
||||||
it.reset()
|
it.reset()
|
||||||
}
|
}
|
||||||
trackers?.let {
|
trackers?.let {
|
||||||
filterItems.remove(it)
|
filterItems.remove(it)
|
||||||
}
|
}
|
||||||
filterItems.remove(unreadProgress)
|
filterItems.remove(allUnread)
|
||||||
reSortViews()
|
reSortViews()
|
||||||
onGroupClicked(ACTION_FILTER)
|
onGroupClicked(ACTION_FILTER)
|
||||||
}
|
}
|
||||||
@ -359,6 +373,11 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
|
|||||||
const val ACTION_FILTER = 1
|
const val ACTION_FILTER = 1
|
||||||
const val ACTION_HIDE_FILTER_TIP = 2
|
const val ACTION_HIDE_FILTER_TIP = 2
|
||||||
const val ACTION_DISPLAY = 3
|
const val ACTION_DISPLAY = 3
|
||||||
|
|
||||||
|
const val STATE_IGNORE = 0
|
||||||
|
const val STATE_INCLUDE = 1
|
||||||
|
const val STATE_EXCLUDE = 2
|
||||||
|
|
||||||
var FILTER_TRACKER = ""
|
var FILTER_TRACKER = ""
|
||||||
private set
|
private set
|
||||||
}
|
}
|
||||||
|
@ -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<Item>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates all the elements of this group. Implementations can override this method for more
|
|
||||||
* customization.
|
|
||||||
*/
|
|
||||||
fun createItems() = (mutableListOf<Item>() + 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<Item>) : RecyclerView.Adapter<Holder>() {
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
@ -115,6 +115,7 @@
|
|||||||
library from the browse tab.</string>
|
library from the browse tab.</string>
|
||||||
<string name="no_matches_for_filters">No matches found for your current filters</string>
|
<string name="no_matches_for_filters">No matches found for your current filters</string>
|
||||||
<string name="show_all_categories">Show all categories</string>
|
<string name="show_all_categories">Show all categories</string>
|
||||||
|
<string name="all_unread">All unread</string>
|
||||||
|
|
||||||
<!-- Library Sort -->
|
<!-- Library Sort -->
|
||||||
<string name="sort_by_">Sort by: %1$s</string>
|
<string name="sort_by_">Sort by: %1$s</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user