Allow excluding categories from library update
Closes #3467, #4661, #1839 Supersedes #4474
This commit is contained in:
parent
b2fee7035f
commit
4f1275ac01
10 changed files with 334 additions and 27 deletions
|
@ -232,11 +232,20 @@ class LibraryUpdateService(
|
||||||
libraryManga.filter { it.category == categoryId }
|
libraryManga.filter { it.category == categoryId }
|
||||||
} else {
|
} else {
|
||||||
val categoriesToUpdate = preferences.libraryUpdateCategories().get().map(String::toInt)
|
val categoriesToUpdate = preferences.libraryUpdateCategories().get().map(String::toInt)
|
||||||
if (categoriesToUpdate.isNotEmpty()) {
|
val listToInclude = if (categoriesToUpdate.isNotEmpty()) {
|
||||||
libraryManga.filter { it.category in categoriesToUpdate }
|
libraryManga.filter { it.category in categoriesToUpdate }
|
||||||
} else {
|
} else {
|
||||||
libraryManga
|
libraryManga
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val categoriesToExclude = preferences.libraryUpdateCategoriesExclude().get().map(String::toInt)
|
||||||
|
val listToExclude = if (categoriesToExclude.isNotEmpty()) {
|
||||||
|
listToInclude.filter { it.category in categoriesToExclude }
|
||||||
|
} else {
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
listToInclude.minus(listToExclude)
|
||||||
}
|
}
|
||||||
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
if (target == Target.CHAPTERS && preferences.updateOnlyNonCompleted()) {
|
||||||
listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED }
|
listToUpdate = listToUpdate.filterNot { it.status == SManga.COMPLETED }
|
||||||
|
|
|
@ -124,6 +124,7 @@ object PreferenceKeys {
|
||||||
const val libraryUpdateRestriction = "library_update_restriction"
|
const val libraryUpdateRestriction = "library_update_restriction"
|
||||||
|
|
||||||
const val libraryUpdateCategories = "library_update_categories"
|
const val libraryUpdateCategories = "library_update_categories"
|
||||||
|
const val libraryUpdateCategoriesExclude = "library_update_categories_exclude"
|
||||||
|
|
||||||
const val libraryUpdatePrioritization = "library_update_prioritization"
|
const val libraryUpdatePrioritization = "library_update_prioritization"
|
||||||
|
|
||||||
|
|
|
@ -218,6 +218,7 @@ class PreferencesHelper(val context: Context) {
|
||||||
fun libraryUpdateRestriction() = prefs.getStringSet(Keys.libraryUpdateRestriction, setOf("wifi"))
|
fun libraryUpdateRestriction() = prefs.getStringSet(Keys.libraryUpdateRestriction, setOf("wifi"))
|
||||||
|
|
||||||
fun libraryUpdateCategories() = flowPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
|
fun libraryUpdateCategories() = flowPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
|
||||||
|
fun libraryUpdateCategoriesExclude() = flowPrefs.getStringSet(Keys.libraryUpdateCategoriesExclude, emptySet())
|
||||||
|
|
||||||
fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0)
|
fun libraryUpdatePrioritization() = flowPrefs.getInt(Keys.libraryUpdatePrioritization, 0)
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,10 @@ import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.core.text.buildSpannedString
|
||||||
import androidx.preference.PreferenceScreen
|
import androidx.preference.PreferenceScreen
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
import com.afollestad.materialdialogs.customview.customView
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
|
@ -29,6 +29,8 @@ import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||||
import eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
import eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||||
|
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox
|
||||||
|
import eu.kanade.tachiyomi.widget.materialdialogs.listItemsQuadStateMultiChoice
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
@ -174,18 +176,37 @@ class SettingsLibraryController : SettingsController() {
|
||||||
LibraryGlobalUpdateCategoriesDialog().showDialog(router)
|
LibraryGlobalUpdateCategoriesDialog().showDialog(router)
|
||||||
}
|
}
|
||||||
|
|
||||||
preferences.libraryUpdateCategories().asFlow()
|
fun updateSummary() {
|
||||||
.onEach { mutableSet ->
|
val selectedCategories = preferences.libraryUpdateCategories().get()
|
||||||
val selectedCategories = mutableSet
|
.mapNotNull { id -> categories.find { it.id == id.toInt() } }
|
||||||
.mapNotNull { id -> categories.find { it.id == id.toInt() } }
|
.sortedBy { it.order }
|
||||||
.sortedBy { it.order }
|
val includedItemsText = if (selectedCategories.isEmpty()) {
|
||||||
|
context.getString(R.string.all)
|
||||||
summary = if (selectedCategories.isEmpty()) {
|
} else {
|
||||||
context.getString(R.string.all)
|
selectedCategories.joinToString { it.name }
|
||||||
} else {
|
|
||||||
selectedCategories.joinToString { it.name }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val excludedCategories = preferences.libraryUpdateCategoriesExclude().get()
|
||||||
|
.mapNotNull { id -> categories.find { it.id == id.toInt() } }
|
||||||
|
.sortedBy { it.order }
|
||||||
|
val excludedItemsText = if (excludedCategories.isEmpty()) {
|
||||||
|
context.getString(R.string.none)
|
||||||
|
} else {
|
||||||
|
excludedCategories.joinToString { it.name }
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = buildSpannedString {
|
||||||
|
append(context.getString(R.string.include, includedItemsText))
|
||||||
|
appendLine()
|
||||||
|
append(context.getString(R.string.exclude, excludedItemsText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
preferences.libraryUpdateCategories().asFlow()
|
||||||
|
.onEach { updateSummary() }
|
||||||
|
.launchIn(viewScope)
|
||||||
|
preferences.libraryUpdateCategoriesExclude().asFlow()
|
||||||
|
.onEach { updateSummary() }
|
||||||
.launchIn(viewScope)
|
.launchIn(viewScope)
|
||||||
}
|
}
|
||||||
intListPreference {
|
intListPreference {
|
||||||
|
@ -281,19 +302,34 @@ class SettingsLibraryController : SettingsController() {
|
||||||
|
|
||||||
val items = categories.map { it.name }
|
val items = categories.map { it.name }
|
||||||
val preselected = categories
|
val preselected = categories
|
||||||
.filter { it.id.toString() in preferences.libraryUpdateCategories().get() }
|
.map {
|
||||||
.map { categories.indexOf(it) }
|
when (it.id.toString()) {
|
||||||
|
in preferences.libraryUpdateCategories().get() -> QuadStateCheckBox.State.CHECKED.ordinal
|
||||||
|
in preferences.libraryUpdateCategoriesExclude().get() -> QuadStateCheckBox.State.INVERSED.ordinal
|
||||||
|
else -> QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||||
|
}
|
||||||
|
}
|
||||||
.toIntArray()
|
.toIntArray()
|
||||||
|
|
||||||
return MaterialDialog(activity!!)
|
return MaterialDialog(activity!!)
|
||||||
.title(R.string.pref_library_update_categories)
|
.title(R.string.pref_library_update_categories)
|
||||||
.listItemsMultiChoice(
|
.listItemsQuadStateMultiChoice(
|
||||||
items = items,
|
items = items,
|
||||||
initialSelection = preselected,
|
initialSelected = preselected
|
||||||
allowEmptySelection = true
|
) { selections ->
|
||||||
) { _, selections, _ ->
|
val included = selections
|
||||||
val newCategories = selections.map { categories[it] }
|
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.CHECKED.ordinal) index else null }
|
||||||
preferences.libraryUpdateCategories().set(newCategories.map { it.id.toString() }.toSet())
|
.filterNotNull()
|
||||||
|
.map { categories[it].id.toString() }
|
||||||
|
.toSet()
|
||||||
|
val excluded = selections
|
||||||
|
.mapIndexed { index, value -> if (value == QuadStateCheckBox.State.INVERSED.ordinal) index else null }
|
||||||
|
.filterNotNull()
|
||||||
|
.map { categories[it].id.toString() }
|
||||||
|
.toSet()
|
||||||
|
|
||||||
|
preferences.libraryUpdateCategories().set(included)
|
||||||
|
preferences.libraryUpdateCategoriesExclude().set(excluded)
|
||||||
}
|
}
|
||||||
.positiveButton(android.R.string.ok)
|
.positiveButton(android.R.string.ok)
|
||||||
.negativeButton(android.R.string.cancel)
|
.negativeButton(android.R.string.cancel)
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||||
|
|
||||||
|
import androidx.annotation.CheckResult
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.list.customListAdapter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A variant of listItemsMultiChoice that allows for checkboxes that supports 4 states instead.
|
||||||
|
*/
|
||||||
|
@CheckResult
|
||||||
|
fun MaterialDialog.listItemsQuadStateMultiChoice(
|
||||||
|
items: List<CharSequence>,
|
||||||
|
disabledIndices: IntArray? = null,
|
||||||
|
initialSelected: IntArray = IntArray(items.size),
|
||||||
|
selection: QuadStateMultiChoiceListener
|
||||||
|
): MaterialDialog {
|
||||||
|
return customListAdapter(
|
||||||
|
QuadStateMultiChoiceDialogAdapter(
|
||||||
|
dialog = this,
|
||||||
|
items = items,
|
||||||
|
disabledItems = disabledIndices,
|
||||||
|
initialSelected = initialSelected,
|
||||||
|
selection = selection
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package eu.kanade.tachiyomi.widget
|
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
|
@ -35,10 +35,11 @@ class QuadStateCheckBox @JvmOverloads constructor(context: Context, attrs: Attri
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class State {
|
enum class State {
|
||||||
object UNCHECKED : State()
|
UNCHECKED,
|
||||||
object INDETERMINATE : State()
|
INDETERMINATE,
|
||||||
object CHECKED : State()
|
CHECKED,
|
||||||
object INVERSED : State()
|
INVERSED,
|
||||||
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.internal.list.DialogAdapter
|
||||||
|
import com.afollestad.materialdialogs.list.getItemSelector
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.inflate
|
||||||
|
import com.afollestad.materialdialogs.utils.MDUtil.maybeSetTextColor
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
private object CheckPayload
|
||||||
|
private object InverseCheckPayload
|
||||||
|
private object UncheckPayload
|
||||||
|
|
||||||
|
typealias QuadStateMultiChoiceListener = (indices: IntArray) -> Unit
|
||||||
|
|
||||||
|
internal class QuadStateMultiChoiceDialogAdapter(
|
||||||
|
private var dialog: MaterialDialog,
|
||||||
|
internal var items: List<CharSequence>,
|
||||||
|
disabledItems: IntArray?,
|
||||||
|
initialSelected: IntArray,
|
||||||
|
internal var selection: QuadStateMultiChoiceListener
|
||||||
|
) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>(),
|
||||||
|
DialogAdapter<CharSequence, QuadStateMultiChoiceListener> {
|
||||||
|
|
||||||
|
private val states = QuadStateCheckBox.State.values()
|
||||||
|
|
||||||
|
private var currentSelection: IntArray = initialSelected
|
||||||
|
set(value) {
|
||||||
|
val previousSelection = field
|
||||||
|
field = value
|
||||||
|
previousSelection.forEachIndexed { index, previous ->
|
||||||
|
val current = value[index]
|
||||||
|
when {
|
||||||
|
current == QuadStateCheckBox.State.CHECKED.ordinal && previous != QuadStateCheckBox.State.CHECKED.ordinal -> {
|
||||||
|
// This value was selected
|
||||||
|
notifyItemChanged(index, CheckPayload)
|
||||||
|
}
|
||||||
|
current == QuadStateCheckBox.State.INVERSED.ordinal && previous != QuadStateCheckBox.State.INVERSED.ordinal -> {
|
||||||
|
// This value was inverse selected
|
||||||
|
notifyItemChanged(index, InverseCheckPayload)
|
||||||
|
}
|
||||||
|
current == QuadStateCheckBox.State.UNCHECKED.ordinal && previous != QuadStateCheckBox.State.UNCHECKED.ordinal -> {
|
||||||
|
// This value was unselected
|
||||||
|
notifyItemChanged(index, UncheckPayload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private var disabledIndices: IntArray = disabledItems ?: IntArray(0)
|
||||||
|
|
||||||
|
internal fun itemClicked(index: Int) {
|
||||||
|
val newSelection = this.currentSelection.toMutableList()
|
||||||
|
newSelection[index] = when (currentSelection[index]) {
|
||||||
|
QuadStateCheckBox.State.CHECKED.ordinal -> QuadStateCheckBox.State.INVERSED.ordinal
|
||||||
|
QuadStateCheckBox.State.INVERSED.ordinal -> QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||||
|
// INDETERMINATE or UNCHECKED
|
||||||
|
else -> QuadStateCheckBox.State.CHECKED.ordinal
|
||||||
|
}
|
||||||
|
this.currentSelection = newSelection.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(
|
||||||
|
parent: ViewGroup,
|
||||||
|
viewType: Int
|
||||||
|
): QuadStateMultiChoiceViewHolder {
|
||||||
|
val listItemView: View = parent.inflate(dialog.windowContext, R.layout.md_listitem_quadstatemultichoice)
|
||||||
|
val viewHolder = QuadStateMultiChoiceViewHolder(
|
||||||
|
itemView = listItemView,
|
||||||
|
adapter = this
|
||||||
|
)
|
||||||
|
viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content)
|
||||||
|
|
||||||
|
return viewHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = items.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: QuadStateMultiChoiceViewHolder,
|
||||||
|
position: Int
|
||||||
|
) {
|
||||||
|
holder.isEnabled = !disabledIndices.contains(position)
|
||||||
|
|
||||||
|
holder.controlView.state = states[currentSelection[position]]
|
||||||
|
holder.titleView.text = items[position]
|
||||||
|
holder.itemView.background = dialog.getItemSelector()
|
||||||
|
|
||||||
|
if (dialog.bodyFont != null) {
|
||||||
|
holder.titleView.typeface = dialog.bodyFont
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(
|
||||||
|
holder: QuadStateMultiChoiceViewHolder,
|
||||||
|
position: Int,
|
||||||
|
payloads: MutableList<Any>
|
||||||
|
) {
|
||||||
|
when (payloads.firstOrNull()) {
|
||||||
|
CheckPayload -> {
|
||||||
|
holder.controlView.state = QuadStateCheckBox.State.CHECKED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
InverseCheckPayload -> {
|
||||||
|
holder.controlView.state = QuadStateCheckBox.State.INVERSED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
UncheckPayload -> {
|
||||||
|
holder.controlView.state = QuadStateCheckBox.State.UNCHECKED
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.onBindViewHolder(holder, position, payloads)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun positiveButtonClicked() {
|
||||||
|
selection.invoke(currentSelection)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replaceItems(
|
||||||
|
items: List<CharSequence>,
|
||||||
|
listener: QuadStateMultiChoiceListener?
|
||||||
|
) {
|
||||||
|
this.items = items
|
||||||
|
if (listener != null) {
|
||||||
|
this.selection = listener
|
||||||
|
}
|
||||||
|
this.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun disableItems(indices: IntArray) {
|
||||||
|
this.disabledIndices = indices
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkItems(indices: IntArray) {
|
||||||
|
val newSelection = this.currentSelection.toMutableList()
|
||||||
|
for (index in indices) {
|
||||||
|
newSelection[index] = QuadStateCheckBox.State.CHECKED.ordinal
|
||||||
|
}
|
||||||
|
this.currentSelection = newSelection.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uncheckItems(indices: IntArray) {
|
||||||
|
val newSelection = this.currentSelection.toMutableList()
|
||||||
|
for (index in indices) {
|
||||||
|
newSelection[index] = QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||||
|
}
|
||||||
|
this.currentSelection = newSelection.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toggleItems(indices: IntArray) {
|
||||||
|
val newSelection = this.currentSelection.toMutableList()
|
||||||
|
for (index in indices) {
|
||||||
|
if (this.disabledIndices.contains(index)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.currentSelection[index] != QuadStateCheckBox.State.CHECKED.ordinal) {
|
||||||
|
newSelection[index] = QuadStateCheckBox.State.CHECKED.ordinal
|
||||||
|
} else {
|
||||||
|
newSelection[index] = QuadStateCheckBox.State.UNCHECKED.ordinal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.currentSelection = newSelection.toIntArray()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun checkAllItems() {
|
||||||
|
this.currentSelection = IntArray(itemCount) { QuadStateCheckBox.State.CHECKED.ordinal }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun uncheckAllItems() {
|
||||||
|
this.currentSelection = IntArray(itemCount) { QuadStateCheckBox.State.UNCHECKED.ordinal }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toggleAllChecked() {
|
||||||
|
if (this.currentSelection.any { it != QuadStateCheckBox.State.CHECKED.ordinal }) {
|
||||||
|
checkAllItems()
|
||||||
|
} else {
|
||||||
|
uncheckAllItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun isItemChecked(index: Int) = this.currentSelection[index] == QuadStateCheckBox.State.CHECKED.ordinal
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||||
|
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
internal class QuadStateMultiChoiceViewHolder(
|
||||||
|
itemView: View,
|
||||||
|
private val adapter: QuadStateMultiChoiceDialogAdapter
|
||||||
|
) : RecyclerView.ViewHolder(itemView), View.OnClickListener {
|
||||||
|
init {
|
||||||
|
itemView.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val controlView: QuadStateCheckBox = itemView.findViewById(R.id.md_quad_state_control)
|
||||||
|
val titleView: TextView = itemView.findViewById(R.id.md_quad_state_title)
|
||||||
|
|
||||||
|
var isEnabled: Boolean
|
||||||
|
get() = itemView.isEnabled
|
||||||
|
set(value) {
|
||||||
|
itemView.isEnabled = value
|
||||||
|
controlView.isEnabled = value
|
||||||
|
titleView.isEnabled = value
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(view: View) = adapter.itemClicked(bindingAdapterPosition)
|
||||||
|
}
|
15
app/src/main/res/layout/md_listitem_quadstatemultichoice.xml
Normal file
15
app/src/main/res/layout/md_listitem_quadstatemultichoice.xml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
style="@style/MD_ListItem.Choice">
|
||||||
|
|
||||||
|
<eu.kanade.tachiyomi.widget.materialdialogs.QuadStateCheckBox
|
||||||
|
android:id="@+id/md_quad_state_control"
|
||||||
|
style="@style/MD_ListItem_Control" />
|
||||||
|
|
||||||
|
<com.afollestad.materialdialogs.internal.rtl.RtlTextView
|
||||||
|
android:id="@+id/md_quad_state_title"
|
||||||
|
style="@style/MD_ListItemText.Choice"
|
||||||
|
tools:text="Item" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -225,6 +225,9 @@
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="pref_library_update_categories">Categories to include in global update</string>
|
<string name="pref_library_update_categories">Categories to include in global update</string>
|
||||||
<string name="all">All</string>
|
<string name="all">All</string>
|
||||||
|
<string name="none">None</string>
|
||||||
|
<string name="include">Include: %s</string>
|
||||||
|
<string name="exclude">Exclude: %s</string>
|
||||||
|
|
||||||
<!-- Extension section -->
|
<!-- Extension section -->
|
||||||
<string name="all_lang">All</string>
|
<string name="all_lang">All</string>
|
||||||
|
|
Reference in a new issue