Improve catalog search filters (#615)

* Add three state (include/exclude/ignore) search filters (works for now only on MangaFox and MangaHere)

* checkbox icons in xml format

* fix checkbox icons referencing

* fix three states filters in remaining catalogs

* use Spinner for filter with more than three states (Mangasee)

* use EditText for freetext filters (Mangasee)

* remove pngs

* Filter class/subclass

* add Filter.Header

* English catalogs
This commit is contained in:
paronos
2017-01-02 18:30:10 +01:00
committed by inorichi
parent 2032ba3ba3
commit d3e9200a7f
22 changed files with 853 additions and 495 deletions

View File

@@ -452,19 +452,21 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
* Show the filter dialog for the source.
*/
private fun showFiltersDialog() {
val allFilters = presenter.source.filters
val selectedFilters = presenter.filters
.map { filter -> allFilters.indexOf(filter) }
.toTypedArray()
val adapter = FilterAdapter(if (presenter.filters.isEmpty()) presenter.source.getFilterList() // make a copy
else presenter.filters)
MaterialDialog.Builder(context)
.title(R.string.action_set_filter)
.items(allFilters.map { it.name })
.itemsCallbackMultiChoice(selectedFilters) { dialog, positions, text ->
val newFilters = positions.map { allFilters[it] }
.adapter(adapter, null)
.onPositive() { dialog, which ->
showProgressBar()
presenter.setSourceFilter(newFilters)
true
var allDefault = true
for (i in 0..adapter.filters.lastIndex) {
if (adapter.filters[i].state != presenter.source.filters[i].state) {
allDefault = false
break
}
}
presenter.setSourceFilter(if (allDefault) emptyList() else adapter.filters)
}
.positiveText(android.R.string.ok)
.negativeText(android.R.string.cancel)

View File

@@ -5,7 +5,7 @@ import eu.kanade.tachiyomi.data.source.online.OnlineSource
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
import rx.Observable
open class CataloguePager(val source: OnlineSource, val query: String, val filters: List<Filter>): Pager() {
open class CataloguePager(val source: OnlineSource, val query: String, val filters: List<Filter<*>>) : Pager() {
override fun requestNext(transformer: (Observable<MangasPage>) -> Observable<MangasPage>): Observable<MangasPage> {
val lastPage = lastPage

View File

@@ -65,9 +65,9 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
private set
/**
* Active filters.
* Filters states.
*/
var filters: List<Filter> = emptyList()
var filters: List<Filter<*>> = emptyList()
/**
* Pager containing a list of manga results.
@@ -128,9 +128,9 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
* Restarts the pager for the active source with the provided query and filters.
*
* @param query the query.
* @param filters the list of active filters (for search mode).
* @param filters the current state of the filters (for search mode).
*/
fun restartPager(query: String = this.query, filters: List<Filter> = this.filters) {
fun restartPager(query: String = this.query, filters: List<Filter<*>> = this.filters) {
this.query = query
this.filters = filters
@@ -362,15 +362,15 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
}
/**
* Set the active filters for the current source.
* Set the filter states for the current source.
*
* @param selectedFilters a list of active filters.
* @param filterStates a list of active filters.
*/
fun setSourceFilter(selectedFilters: List<Filter>) {
restartPager(filters = selectedFilters)
fun setSourceFilter(filters: List<Filter<*>>) {
restartPager(filters = filters)
}
open fun createPager(query: String, filters: List<Filter>): Pager {
open fun createPager(query: String, filters: List<Filter<*>>): Pager {
return CataloguePager(source, query, filters)
}

View File

@@ -0,0 +1,153 @@
package eu.kanade.tachiyomi.ui.catalogue
import android.content.Context
import android.graphics.Typeface
import android.support.graphics.drawable.VectorDrawableCompat
import android.support.v7.widget.RecyclerView
import android.view.View
import android.view.ViewGroup
import android.widget.*
import android.widget.AdapterView.OnItemSelectedListener
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
import android.text.TextWatcher
import android.text.Editable
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import eu.kanade.tachiyomi.util.inflate
class FilterAdapter(val filters: List<Filter<*>>) : RecyclerView.Adapter<FilterAdapter.ViewHolder>() {
private companion object {
const val HEADER = 0
const val CHECKBOX = 1
const val TRISTATE = 2
const val LIST = 3
const val TEXT = 4
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FilterAdapter.ViewHolder {
return when (viewType) {
HEADER -> ViewHolder(SepText(parent))
LIST -> ViewHolder(TextSpinner(parent.context))
TEXT -> ViewHolder(TextEditText(parent.context))
else -> ViewHolder(CheckBox(parent.context))
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val filter = filters[position]
when (filter) {
is Filter.Header -> {
if (filter.name.isEmpty()) (holder.view as SepText).textView.visibility = View.GONE
else (holder.view as SepText).textView.text = filter.name
}
is Filter.CheckBox -> {
var checkBox = holder.view as CheckBox
checkBox.text = filter.name
checkBox.isChecked = filter.state
checkBox.setButtonDrawable(VectorDrawableCompat.create(checkBox.getResources(), R.drawable.ic_check_box_set, null))
checkBox.setOnCheckedChangeListener { buttonView, isChecked ->
filter.state = isChecked
}
}
is Filter.TriState -> {
var triCheckBox = holder.view as CheckBox
triCheckBox.text = filter.name
val icons = arrayOf(VectorDrawableCompat.create(triCheckBox.getResources(), R.drawable.ic_check_box_outline_blank_24dp, null),
VectorDrawableCompat.create(triCheckBox.getResources(), R.drawable.ic_check_box_24dp, null),
VectorDrawableCompat.create(triCheckBox.getResources(), R.drawable.ic_check_box_x_24dp, null))
triCheckBox.setButtonDrawable(icons[filter.state])
triCheckBox.invalidate()
triCheckBox.setOnCheckedChangeListener { buttonView, isChecked ->
filter.state = (filter.state + 1) % 3
triCheckBox.setButtonDrawable(icons[filter.state])
triCheckBox.invalidate()
}
}
is Filter.List<*> -> {
var txtSpin = holder.view as TextSpinner
if (filter.name.isEmpty()) txtSpin.textView.visibility = View.GONE
else txtSpin.textView.text = filter.name + ":"
txtSpin.spinner.adapter = ArrayAdapter<Any>(holder.view.context,
android.R.layout.simple_spinner_item, filter.values)
txtSpin.spinner.setSelection(filter.state)
txtSpin.spinner.onItemSelectedListener = object : OnItemSelectedListener {
override fun onItemSelected(parentView: AdapterView<*>, selectedItemView: View, pos: Int, id: Long) {
filter.state = pos
}
override fun onNothingSelected(parentView: AdapterView<*>) {
}
}
}
is Filter.Text -> {
var txtEdTx = holder.view as TextEditText
if (filter.name.isEmpty()) txtEdTx.textView.visibility = View.GONE
else txtEdTx.textView.text = filter.name + ":"
txtEdTx.editText.setText(filter.state)
txtEdTx.editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(s: Editable) {
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
filter.state = s.toString()
}
})
}
}
}
override fun getItemCount(): Int {
return filters.size
}
override fun getItemViewType(position: Int): Int {
return when (filters[position]) {
is Filter.Header -> HEADER
is Filter.CheckBox -> CHECKBOX
is Filter.TriState -> TRISTATE
is Filter.List<*> -> LIST
is Filter.Text -> TEXT
}
}
class ViewHolder(val view: View) : RecyclerView.ViewHolder(view)
private class SepText(parent: ViewGroup) : LinearLayout(parent.context) {
val separator: View = parent.inflate(R.layout.design_navigation_item_separator)
val textView: TextView = TextView(context)
init {
orientation = LinearLayout.VERTICAL
textView.setTypeface(null, Typeface.BOLD);
addView(separator)
addView(textView)
}
}
private class TextSpinner(context: Context?) : LinearLayout(context) {
val textView: TextView = TextView(context)
val spinner: Spinner = Spinner(context)
init {
addView(textView)
addView(spinner)
}
}
private class TextEditText(context: Context?) : LinearLayout(context) {
val textView: TextView = TextView(context)
val editText: EditText = EditText(context)
init {
addView(textView)
editText.setSingleLine()
editText.setImeOptions(EditorInfo.IME_ACTION_DONE);
addView(editText)
}
}
}

View File

@@ -11,7 +11,7 @@ import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
*/
class LatestUpdatesPresenter : CataloguePresenter() {
override fun createPager(query: String, filters: List<Filter>): Pager {
override fun createPager(query: String, filters: List<Filter<*>>): Pager {
return LatestUpdatesPager(source)
}