Explorar o código

Add Sort filter [Catalogs] (#633)

* Add Sort filter

* remove old views

* onClick default descending

* update remaining catalogs
paronos %!s(int64=8) %!d(string=hai) anos
pai
achega
a03dceff7d

+ 7 - 0
app/src/main/java/eu/kanade/tachiyomi/data/source/model/Filter.kt

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.source.model
 
 
 sealed class Filter<T>(val name: String, var state: T) {
 sealed class Filter<T>(val name: String, var state: T) {
     open class Header(name: String) : Filter<Any>(name, 0)
     open class Header(name: String) : Filter<Any>(name, 0)
+    open class Separator(name: String = "") : Filter<Any>(name, 0)
     abstract class List<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
     abstract class List<V>(name: String, val values: Array<V>, state: Int = 0) : Filter<Int>(name, state)
     abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
     abstract class Text(name: String, state: String = "") : Filter<String>(name, state)
     abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
     abstract class CheckBox(name: String, state: Boolean = false) : Filter<Boolean>(name, state)
@@ -9,10 +10,16 @@ sealed class Filter<T>(val name: String, var state: T) {
         fun isIgnored() = state == STATE_IGNORE
         fun isIgnored() = state == STATE_IGNORE
         fun isIncluded() = state == STATE_INCLUDE
         fun isIncluded() = state == STATE_INCLUDE
         fun isExcluded() = state == STATE_EXCLUDE
         fun isExcluded() = state == STATE_EXCLUDE
+
         companion object {
         companion object {
             const val STATE_IGNORE = 0
             const val STATE_IGNORE = 0
             const val STATE_INCLUDE = 1
             const val STATE_INCLUDE = 1
             const val STATE_EXCLUDE = 2
             const val STATE_EXCLUDE = 2
         }
         }
     }
     }
+
+    abstract class Sort<V>(name: String, val values: Array<V>, state: Selection? = null)
+        : Filter<Sort.Selection?>(name, state) {
+        data class Selection(val index: Int, val ascending: Boolean)
+    }
 }
 }

+ 10 - 2
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt

@@ -107,6 +107,10 @@ class Batoto : ParsedOnlineSource(), LoginSource {
                     val sel = if (filter.state) filter.valTrue else filter.valFalse
                     val sel = if (filter.state) filter.valTrue else filter.valFalse
                     if (!sel.isEmpty()) url.addQueryParameter(filter.key, sel)
                     if (!sel.isEmpty()) url.addQueryParameter(filter.key, sel)
                 }
                 }
+                is OrderBy -> {
+                    url.addQueryParameter("order_cond", arrayOf("title", "author", "artist", "rating", "views", "update")[filter.state!!.index])
+                    url.addQueryParameter("order", if (filter.state?.ascending == true) "asc" else "desc")
+                }
             }
             }
         }
         }
         if (!genres.isEmpty()) url.addQueryParameter("genres", genres)
         if (!genres.isEmpty()) url.addQueryParameter("genres", genres)
@@ -288,6 +292,9 @@ class Batoto : ParsedOnlineSource(), LoginSource {
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
     private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
     private class Flag(name: String, val key: String, val valTrue: String, val valFalse: String) : Filter.CheckBox(name)
     private class Flag(name: String, val key: String, val valTrue: String, val valFalse: String) : Filter.CheckBox(name)
+    private class OrderBy() : Filter.Sort<String>("Order by",
+            arrayOf("Title", "Author", "Artist", "Rating", "Views", "Last Update"),
+            Filter.Sort.Selection(4, false))
 
 
     // [...document.querySelectorAll("#advanced_options div.genre_buttons")].map((el,i) => {
     // [...document.querySelectorAll("#advanced_options div.genre_buttons")].map((el,i) => {
     //     const onClick=el.getAttribute('onclick');const id=onClick.substr(14,onClick.length-16);return `Genre("${el.textContent.trim()}", ${id})`
     //     const onClick=el.getAttribute('onclick');const id=onClick.substr(14,onClick.length-16);return `Genre("${el.textContent.trim()}", ${id})`
@@ -298,8 +305,9 @@ class Batoto : ParsedOnlineSource(), LoginSource {
             ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Manga (Jp)", "jp"), ListValue("Manhwa (Kr)", "kr"), ListValue("Manhua (Cn)", "cn"), ListValue("Artbook", "ar"), ListValue("Other", "ot"))),
             ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Manga (Jp)", "jp"), ListValue("Manhwa (Kr)", "kr"), ListValue("Manhua (Cn)", "cn"), ListValue("Artbook", "ar"), ListValue("Other", "ot"))),
             Status(),
             Status(),
             Flag("Exclude mature", "mature", "m", ""),
             Flag("Exclude mature", "mature", "m", ""),
-            ListField("Order by", "order_cond", arrayOf(ListValue("Title", "title"), ListValue("Author", "author"), ListValue("Artist", "artist"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Last Update", "update")), 4),
-            Flag("Ascending order", "order", "asc", "desc"),
+            Filter.Separator(),
+            OrderBy(),
+            Filter.Separator(),
             Filter.Header("Genres"),
             Filter.Header("Genres"),
             ListField("Inclusion mode", "genre_cond", arrayOf(ListValue("And (all selected genres)", "and"), ListValue("Or (any selected genres) ", "or"))),
             ListField("Inclusion mode", "genre_cond", arrayOf(ListValue("And (all selected genres)", "and"), ListValue("Or (any selected genres) ", "or"))),
             Genre("4-Koma", 40),
             Genre("4-Koma", 40),

+ 14 - 12
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt

@@ -24,7 +24,7 @@ class Mangafox : ParsedOnlineSource() {
     override val supportsLatest = true
     override val supportsLatest = true
 
 
     override fun popularMangaSelector() = "div#mangalist > ul.list > li"
     override fun popularMangaSelector() = "div#mangalist > ul.list > li"
-    
+
     override fun popularMangaRequest(page: Int): Request {
     override fun popularMangaRequest(page: Int): Request {
         val pageStr = if (page != 1) "$page.htm" else ""
         val pageStr = if (page != 1) "$page.htm" else ""
         return GET("$baseUrl/directory/$pageStr", headers)
         return GET("$baseUrl/directory/$pageStr", headers)
@@ -60,8 +60,11 @@ class Mangafox : ParsedOnlineSource() {
             when (filter) {
             when (filter) {
                 is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
                 is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
                 is TextField -> url.addQueryParameter(filter.key, filter.state)
                 is TextField -> url.addQueryParameter(filter.key, filter.state)
-                is ListField -> url.addQueryParameter(filter.key, filter.values[filter.state].value)
-                is Order -> url.addQueryParameter("order", if (filter.state) "az" else "za")
+                is Type -> url.addQueryParameter("type", if(filter.state == 0) "" else filter.state.toString())
+                is OrderBy -> {
+                    url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
+                    url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
+                }
             }
             }
         }
         }
         url.addQueryParameter("page", page.toString())
         url.addQueryParameter("page", page.toString())
@@ -158,24 +161,23 @@ class Mangafox : ParsedOnlineSource() {
         }
         }
     }
     }
 
 
-    private data class ListValue(val name: String, val value: String) {
-        override fun toString(): String = name
-    }
-
     private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
     private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
-    private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
-    private class Order() : Filter.CheckBox("Ascending order")
+    private class Type() : Filter.List<String>("Type", arrayOf("Any", "Japanese Manga", "Korean Manhwa", "Chinese Manhua"))
+    private class OrderBy() : Filter.Sort<String>("Order by",
+            arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
+            Filter.Sort.Selection(2, false))
 
 
     // $('select.genres').map((i,el)=>`Genre("${$(el).next().text().trim()}", "${$(el).attr('name')}")`).get().join(',\n')
     // $('select.genres').map((i,el)=>`Genre("${$(el).next().text().trim()}", "${$(el).attr('name')}")`).get().join(',\n')
     // on http://mangafox.me/search.php
     // on http://mangafox.me/search.php
     override fun getFilterList() = FilterList(
     override fun getFilterList() = FilterList(
             TextField("Author", "author"),
             TextField("Author", "author"),
             TextField("Artist", "artist"),
             TextField("Artist", "artist"),
-            ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Japanese Manga", "1"), ListValue("Korean Manhwa", "2"), ListValue("Chinese Manhua", "3"))),
+            Type(),
             Genre("Completed", "is_completed"),
             Genre("Completed", "is_completed"),
-            ListField("Order by", "sort", arrayOf(ListValue("Series name", "name"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Total chapters", "total_chapters"), ListValue("Last chapter", "last_chapter_time")), 2),
-            Order(),
+            Filter.Separator(),
+            OrderBy(),
+            Filter.Separator(),
             Filter.Header("Genres"),
             Filter.Header("Genres"),
             Genre("Action"),
             Genre("Action"),
             Genre("Adult"),
             Genre("Adult"),

+ 13 - 7
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt

@@ -63,8 +63,11 @@ class Mangahere : ParsedOnlineSource() {
                 is Status -> url.addQueryParameter("is_completed", arrayOf("", "1", "0")[filter.state])
                 is Status -> url.addQueryParameter("is_completed", arrayOf("", "1", "0")[filter.state])
                 is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
                 is Genre -> url.addQueryParameter(filter.id, filter.state.toString())
                 is TextField -> url.addQueryParameter(filter.key, filter.state)
                 is TextField -> url.addQueryParameter(filter.key, filter.state)
-                is ListField -> url.addQueryParameter(filter.key, filter.values[filter.state].value)
-                is Order -> url.addQueryParameter("order", if (filter.state) "az" else "za")
+                is Type -> url.addQueryParameter("direction", arrayOf("", "rl", "lr")[filter.state])
+                is OrderBy -> {
+                    url.addQueryParameter("sort", arrayOf("name", "rating", "views", "total_chapters", "last_chapter_time")[filter.state!!.index])
+                    url.addQueryParameter("order", if (filter.state?.ascending == true) "az" else "za")
+                }
             }
             }
         }
         }
         url.addQueryParameter("page", page.toString())
         url.addQueryParameter("page", page.toString())
@@ -166,18 +169,21 @@ class Mangahere : ParsedOnlineSource() {
     private class Status() : Filter.TriState("Completed")
     private class Status() : Filter.TriState("Completed")
     private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
     private class Genre(name: String, val id: String = "genres[$name]") : Filter.TriState(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
-    private class ListField(name: String, val key: String, values: Array<ListValue>, state: Int = 0) : Filter.List<ListValue>(name, values, state)
-    private class Order() : Filter.CheckBox("Ascending order")
+    private class Type() : Filter.List<String>("Type", arrayOf("Any", "Japanese Manga (read from right to left)", "Korean Manhwa (read from left to right)"))
+    private class OrderBy() : Filter.Sort<String>("Order by",
+            arrayOf("Series name", "Rating", "Views", "Total chapters", "Last chapter"),
+            Filter.Sort.Selection(2, false))
 
 
     // [...document.querySelectorAll("select[id^='genres'")].map((el,i) => `Genre("${el.nextSibling.nextSibling.textContent.trim()}", "${el.getAttribute('name')}")`).join(',\n')
     // [...document.querySelectorAll("select[id^='genres'")].map((el,i) => `Genre("${el.nextSibling.nextSibling.textContent.trim()}", "${el.getAttribute('name')}")`).join(',\n')
     // http://www.mangahere.co/advsearch.htm
     // http://www.mangahere.co/advsearch.htm
     override fun getFilterList() = FilterList(
     override fun getFilterList() = FilterList(
             TextField("Author", "author"),
             TextField("Author", "author"),
             TextField("Artist", "artist"),
             TextField("Artist", "artist"),
-            ListField("Type", "direction", arrayOf(ListValue("Any", ""), ListValue("Japanese Manga (read from right to left)", "rl"), ListValue("Korean Manhwa (read from left to right)", "lr"))),
+            Type(),
             Status(),
             Status(),
-            ListField("Order by", "sort", arrayOf(ListValue("Series name", "name"), ListValue("Rating", "rating"), ListValue("Views", "views"), ListValue("Total chapters", "total_chapters"), ListValue("Last chapter", "last_chapter_time")), 2),
-            Order(),
+            Filter.Separator(),
+            OrderBy(),
+            Filter.Separator(),
             Filter.Header("Genres"),
             Filter.Header("Genres"),
             Genre("Action"),
             Genre("Action"),
             Genre("Adventure"),
             Genre("Adventure"),

+ 13 - 14
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangasee.kt

@@ -30,7 +30,7 @@ class Mangasee : ParsedOnlineSource() {
     override fun popularMangaSelector() = "div.requested > div.row"
     override fun popularMangaSelector() = "div.requested > div.row"
 
 
     override fun popularMangaRequest(page: Int): Request {
     override fun popularMangaRequest(page: Int): Request {
-        val (body, requestUrl) = convertQueryToPost(page, "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending&todo=1")
+        val (body, requestUrl) = convertQueryToPost(page, "$baseUrl/search/request.php?sortBy=popularity&sortOrder=descending")
         return POST(requestUrl, headers, body.build())
         return POST(requestUrl, headers, body.build())
     }
     }
 
 
@@ -54,14 +54,17 @@ class Mangasee : ParsedOnlineSource() {
         var genresNo: String? = null
         var genresNo: String? = null
         for (filter in if (filters.isEmpty()) getFilterList() else filters) {
         for (filter in if (filters.isEmpty()) getFilterList() else filters) {
             when (filter) {
             when (filter) {
-                is Sort -> filter.values[filter.state].keys.forEachIndexed { i, s ->
-                    url.addQueryParameter(s, filter.values[filter.state].values[i])
+                is Sort -> {
+                    if (filter.state?.index != 0)
+                        url.addQueryParameter("sortBy", if (filter.state?.index == 1) "dateUpdated" else "popularity")
+                    if (filter.state?.ascending != true)
+                        url.addQueryParameter("sortOrder", "descending")
                 }
                 }
                 is ListField -> if (filter.state != 0) url.addQueryParameter(filter.key, filter.values[filter.state])
                 is ListField -> if (filter.state != 0) url.addQueryParameter(filter.key, filter.values[filter.state])
                 is TextField -> if (!filter.state.isEmpty()) url.addQueryParameter(filter.key, filter.state)
                 is TextField -> if (!filter.state.isEmpty()) url.addQueryParameter(filter.key, filter.state)
                 is Genre -> when (filter.state) {
                 is Genre -> when (filter.state) {
-                    Filter.TriState.STATE_INCLUDE -> genres = if (genres == null) filter.id else genres + "," + filter.id
-                    Filter.TriState.STATE_EXCLUDE -> genresNo = if (genresNo == null) filter.id else genresNo + "," + filter.id
+                    Filter.TriState.STATE_INCLUDE -> genres = if (genres == null) filter.name else genres + "," + filter.name
+                    Filter.TriState.STATE_EXCLUDE -> genresNo = if (genresNo == null) filter.name else genresNo + "," + filter.name
                 }
                 }
             }
             }
         }
         }
@@ -156,8 +159,8 @@ class Mangasee : ParsedOnlineSource() {
         override fun toString(): String = name
         override fun toString(): String = name
     }
     }
 
 
-    private class Sort(name: String, values: Array<SortOption>, state: Int = 0) : Filter.List<SortOption>(name, values, state)
-    private class Genre(name: String, val id: String = name.replace(' ', '_')) : Filter.TriState(name)
+    private class Sort() : Filter.Sort<String>("Sort", arrayOf("Alphabetically", "Date updated", "Popularity"), Filter.Sort.Selection(2, false))
+    private class Genre(name: String) : Filter.TriState(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class TextField(name: String, val key: String) : Filter.Text(name)
     private class ListField(name: String, val key: String, values: Array<String>, state: Int = 0) : Filter.List<String>(name, values, state)
     private class ListField(name: String, val key: String, values: Array<String>, state: Int = 0) : Filter.List<String>(name, values, state)
 
 
@@ -166,16 +169,12 @@ class Mangasee : ParsedOnlineSource() {
     override fun getFilterList() = FilterList(
     override fun getFilterList() = FilterList(
             TextField("Years", "year"),
             TextField("Years", "year"),
             TextField("Author", "author"),
             TextField("Author", "author"),
-            Sort("Sort By", arrayOf(SortOption("Alphabetical A-Z", emptyArray(), emptyArray()),
-                    SortOption("Alphabetical Z-A", arrayOf("sortOrder"), arrayOf("descending")),
-                    SortOption("Newest", arrayOf("sortBy", "sortOrder"), arrayOf("dateUpdated", "descending")),
-                    SortOption("Oldest", arrayOf("sortBy"), arrayOf("dateUpdated")),
-                    SortOption("Most Popular", arrayOf("sortBy", "sortOrder"), arrayOf("popularity", "descending")),
-                    SortOption("Least Popular", arrayOf("sortBy"), arrayOf("popularity"))
-            ), 4),
             ListField("Scan Status", "status", arrayOf("Any", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing")),
             ListField("Scan Status", "status", arrayOf("Any", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing")),
             ListField("Publish Status", "pstatus", arrayOf("Any", "Cancelled", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing", "Unfinished")),
             ListField("Publish Status", "pstatus", arrayOf("Any", "Cancelled", "Complete", "Discontinued", "Hiatus", "Incomplete", "Ongoing", "Unfinished")),
             ListField("Type", "type", arrayOf("Any", "Doujinshi", "Manga", "Manhua", "Manhwa", "OEL", "One-shot")),
             ListField("Type", "type", arrayOf("Any", "Doujinshi", "Manga", "Manhua", "Manhwa", "OEL", "One-shot")),
+            Filter.Separator(),
+            Sort(),
+            Filter.Separator(),
             Filter.Header("Genres"),
             Filter.Header("Genres"),
             Genre("Action"),
             Genre("Action"),
             Genre("Adult"),
             Genre("Adult"),

+ 51 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt

@@ -2,12 +2,12 @@ package eu.kanade.tachiyomi.ui.catalogue
 
 
 import android.content.Context
 import android.content.Context
 import android.support.graphics.drawable.VectorDrawableCompat
 import android.support.graphics.drawable.VectorDrawableCompat
+import android.support.v4.content.ContextCompat
 import android.support.v7.widget.RecyclerView
 import android.support.v7.widget.RecyclerView
 import android.util.AttributeSet
 import android.util.AttributeSet
 import android.view.View
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewGroup
-import android.widget.ArrayAdapter
-import android.widget.TextView
+import android.widget.*
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.source.model.Filter
 import eu.kanade.tachiyomi.data.source.model.Filter
 import eu.kanade.tachiyomi.data.source.model.FilterList
 import eu.kanade.tachiyomi.data.source.model.FilterList
@@ -55,16 +55,19 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
         override fun getItemViewType(position: Int): Int {
         override fun getItemViewType(position: Int): Int {
             return when (items[position]) {
             return when (items[position]) {
                 is Filter.Header -> VIEW_TYPE_HEADER
                 is Filter.Header -> VIEW_TYPE_HEADER
+                is Filter.Separator -> VIEW_TYPE_SEPARATOR
                 is Filter.CheckBox -> VIEW_TYPE_CHECKBOX
                 is Filter.CheckBox -> VIEW_TYPE_CHECKBOX
                 is Filter.TriState -> VIEW_TYPE_MULTISTATE
                 is Filter.TriState -> VIEW_TYPE_MULTISTATE
                 is Filter.List<*> -> VIEW_TYPE_LIST
                 is Filter.List<*> -> VIEW_TYPE_LIST
                 is Filter.Text -> VIEW_TYPE_TEXT
                 is Filter.Text -> VIEW_TYPE_TEXT
+                is Filter.Sort<*> -> VIEW_TYPE_SORT
             }
             }
         }
         }
 
 
         override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
         override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
             return when (viewType) {
             return when (viewType) {
                 VIEW_TYPE_HEADER -> HeaderHolder(parent)
                 VIEW_TYPE_HEADER -> HeaderHolder(parent)
+                VIEW_TYPE_SEPARATOR -> SeparatorHolder(parent)
                 VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, null)
                 VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, null)
                 VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, null).apply {
                 VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, null).apply {
                     // Adjust view with checkbox
                     // Adjust view with checkbox
@@ -73,6 +76,7 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
                 }
                 }
                 VIEW_TYPE_LIST -> SpinnerHolder(parent)
                 VIEW_TYPE_LIST -> SpinnerHolder(parent)
                 VIEW_TYPE_TEXT -> EditTextHolder(parent)
                 VIEW_TYPE_TEXT -> EditTextHolder(parent)
+                VIEW_TYPE_SORT -> SortHolder(parent)
                 else -> throw Exception("Unknown view type")
                 else -> throw Exception("Unknown view type")
             }
             }
         }
         }
@@ -144,9 +148,54 @@ class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs:
                         }
                         }
                     })
                     })
                 }
                 }
+                is Filter.Sort<*> -> {
+                    val view = (holder as SortHolder).sortView
+                    view.removeAllViews()
+                    if (!filter.name.isEmpty()) {
+                        val header = HeaderHolder(view)
+                        (header.itemView as TextView).text = filter.name
+                        view.addView(header.itemView)
+                    }
+                    val holders = Array<MultiStateHolder>(filter.values.size, { MultiStateHolder(view, null) })
+                    for ((i, rb) in holders.withIndex()) {
+                        rb.text.text = filter.values[i].toString()
+
+                        fun getIcon() = when (filter.state) {
+                            Filter.Sort.Selection(i, false) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_keyboard_arrow_down_black_32dp, null)
+                                    ?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
+                            Filter.Sort.Selection(i, true) -> VectorDrawableCompat.create(view.resources, R.drawable.ic_keyboard_arrow_up_black_32dp, null)
+                                    ?.apply { setTint(view.context.getResourceColor(R.attr.colorAccent)) }
+                            else -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp)
+                        }
+
+                        rb.text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
+                        rb.itemView.setOnClickListener {
+                            val pre = filter.state?.index ?: i
+                            if (pre != i) {
+                                holders[pre].text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
+                                filter.state = Filter.Sort.Selection(i, false)
+                            } else {
+                                filter.state = Filter.Sort.Selection(i, filter.state?.ascending == false)
+                            }
+                            rb.text.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
+                        }
+
+                        view.addView(rb.itemView)
+                    }
+                }
             }
             }
         }
         }
 
 
     }
     }
 
 
+    val VIEW_TYPE_SORT = 0
+
+    private class SortHolder(parent: ViewGroup, val sortView: SortView = SortView(parent)) : Holder(sortView) {
+        class SortView(parent: ViewGroup) : LinearLayout(parent.context) {
+            init {
+                orientation = LinearLayout.VERTICAL
+            }
+        }
+    }
+
 }
 }