浏览代码

Change filters dialog with a drawer

len 8 年之前
父节点
当前提交
c25af3d5ad

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

@@ -343,7 +343,6 @@ class Batoto(override val id: Int) : 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"))),
             Status(),
             Flag("Exclude mature", "mature", "m", ""),
-            Filter.Header(""),
             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.Header("Genres"),

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

@@ -155,7 +155,6 @@ class Mangafox(override val id: Int) : ParsedOnlineSource() {
             TextField("Artist", "artist"),
             ListField("Type", "type", arrayOf(ListValue("Any", ""), ListValue("Japanese Manga", "1"), ListValue("Korean Manhwa", "2"), ListValue("Chinese Manhua", "3"))),
             Genre("Completed", "is_completed"),
-            Filter.Header(""),
             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.Header("Genres"),

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

@@ -162,7 +162,6 @@ class Mangahere(override val id: Int) : ParsedOnlineSource() {
             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"))),
             Status(),
-            Filter.Header(""),
             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.Header("Genres"),

+ 59 - 26
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt

@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.catalogue
 import android.content.res.Configuration
 import android.os.Bundle
 import android.support.design.widget.Snackbar
+import android.support.v4.widget.DrawerLayout
 import android.support.v7.widget.*
 import android.view.*
 import android.view.animation.AnimationUtils
@@ -18,10 +19,12 @@ import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
 import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
 import eu.kanade.tachiyomi.ui.main.MainActivity
 import eu.kanade.tachiyomi.ui.manga.MangaActivity
+import eu.kanade.tachiyomi.util.inflate
 import eu.kanade.tachiyomi.util.snack
 import eu.kanade.tachiyomi.util.toast
 import eu.kanade.tachiyomi.widget.EndlessScrollListener
 import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
+import kotlinx.android.synthetic.main.activity_main.*
 import kotlinx.android.synthetic.main.fragment_catalogue.*
 import kotlinx.android.synthetic.main.toolbar.*
 import nucleus.factory.RequiresPresenter
@@ -100,6 +103,30 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
     private val toolbar: Toolbar
         get() = (activity as MainActivity).toolbar
 
+    /**
+     * Navigation view containing filter items.
+     */
+    private var navView: CatalogueNavigationView? = null
+
+    /**
+     * Drawer listener to allow swipe only for closing the drawer.
+     */
+    private val drawerListener by lazy {
+        object : DrawerLayout.SimpleDrawerListener() {
+            override fun onDrawerClosed(drawerView: View) {
+                if (drawerView == navView) {
+                    activity.drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, navView)
+                }
+            }
+
+            override fun onDrawerOpened(drawerView: View) {
+                if (drawerView == navView) {
+                    activity.drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, navView)
+                }
+            }
+        }
+    }
+
     companion object {
         /**
          * Creates a new instance of this fragment.
@@ -176,6 +203,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
                 glm.scrollToPositionWithOffset(0, 0)
                 llm.scrollToPositionWithOffset(0, 0)
                 presenter.setActiveSource(source)
+                navView?.setFilters(presenter.sourceFilters)
                 activity.invalidateOptionsMenu()
             }
         }
@@ -191,6 +219,32 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
         setToolbarTitle("")
         toolbar.addView(spinner)
 
+        // Inflate and prepare drawer
+        val navView = activity.drawer.inflate(R.layout.catalogue_drawer) as CatalogueNavigationView
+        this.navView = navView
+        activity.drawer.addView(navView)
+        activity.drawer.addDrawerListener(drawerListener)
+        navView.setFilters(presenter.sourceFilters)
+
+        navView.post {
+            if (isAdded && !activity.drawer.isDrawerOpen(navView))
+                activity.drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, navView)
+        }
+
+        navView.onSearchClicked = {
+            val allDefault = (0..navView.adapter.items.lastIndex)
+                    .none { navView.adapter.items[it].state != presenter.source.filters[it].state }
+
+            presenter.setSourceFilter(if (allDefault) emptyList() else navView.adapter.items)
+        }
+
+        navView.onResetClicked = {
+            presenter.appliedFilters = emptyList()
+            val newFilters = presenter.source.getFilterList()
+            presenter.sourceFilters = newFilters
+            navView.setFilters(newFilters)
+        }
+
         showProgressBar()
     }
 
@@ -244,7 +298,7 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
     override fun onOptionsItemSelected(item: MenuItem): Boolean {
         when (item.itemId) {
             R.id.action_display_mode -> swapDisplayMode()
-            R.id.action_set_filter -> showFiltersDialog()
+            R.id.action_set_filter -> navView?.let { activity.drawer.openDrawer(Gravity.END) }
             else -> return super.onOptionsItemSelected(item)
         }
         return true
@@ -263,6 +317,10 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
     }
 
     override fun onDestroyView() {
+        navView?.let {
+            activity.drawer.removeDrawerListener(drawerListener)
+            activity.drawer.removeView(it)
+        }
         numColumnsSubscription?.unsubscribe()
         searchItem?.let {
             if (it.isActionViewExpanded) it.collapseActionView()
@@ -448,29 +506,4 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(), FlexibleVie
                 }.show()
     }
 
-    /**
-     * Show the filter dialog for the source.
-     */
-    private fun showFiltersDialog() {
-        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)
-                .adapter(adapter, null)
-                .onPositive() { dialog, which ->
-                    showProgressBar()
-                    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)
-                .show()
-    }
-
 }

+ 151 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt

@@ -0,0 +1,151 @@
+package eu.kanade.tachiyomi.ui.catalogue
+
+import android.content.Context
+import android.support.graphics.drawable.VectorDrawableCompat
+import android.support.v7.widget.RecyclerView
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ArrayAdapter
+import android.widget.TextView
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.source.online.OnlineSource.Filter
+import eu.kanade.tachiyomi.util.dpToPx
+import eu.kanade.tachiyomi.util.getResourceColor
+import eu.kanade.tachiyomi.util.inflate
+import eu.kanade.tachiyomi.widget.IgnoreFirstSpinnerListener
+import eu.kanade.tachiyomi.widget.SimpleNavigationView
+import eu.kanade.tachiyomi.widget.SimpleTextWatcher
+import kotlinx.android.synthetic.main.catalogue_drawer_content.view.*
+
+
+class CatalogueNavigationView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null)
+    : SimpleNavigationView(context, attrs) {
+
+    val adapter = Adapter()
+
+    var onSearchClicked = {}
+
+    var onResetClicked = {}
+
+    init {
+        recycler.adapter = adapter
+        val view = inflate(R.layout.catalogue_drawer_content)
+        (view as ViewGroup).addView(recycler)
+        addView(view)
+
+        search_btn.setOnClickListener { onSearchClicked() }
+        reset_btn.setOnClickListener { onResetClicked() }
+    }
+
+    fun setFilters(items: List<Filter<*>>) {
+        adapter.items = items
+        adapter.notifyDataSetChanged()
+    }
+
+    inner class Adapter : RecyclerView.Adapter<Holder>() {
+
+        var items: List<Filter<*>> = emptyList()
+
+        override fun getItemCount(): Int {
+            return items.size
+        }
+
+        override fun getItemViewType(position: Int): Int {
+            return when (items[position]) {
+                is Filter.Header -> VIEW_TYPE_HEADER
+                is Filter.CheckBox -> VIEW_TYPE_CHECKBOX
+                is Filter.TriState -> VIEW_TYPE_MULTISTATE
+                is Filter.List<*> -> VIEW_TYPE_LIST
+                is Filter.Text -> VIEW_TYPE_TEXT
+            }
+        }
+
+        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
+            return when (viewType) {
+                VIEW_TYPE_HEADER -> HeaderHolder(parent)
+                VIEW_TYPE_CHECKBOX -> CheckboxHolder(parent, null)
+                VIEW_TYPE_MULTISTATE -> MultiStateHolder(parent, null).apply {
+                    // Adjust view with checkbox
+                    text.setPadding(4.dpToPx, 0, 0, 0)
+                    text.compoundDrawablePadding = 20.dpToPx
+                }
+                VIEW_TYPE_LIST -> SpinnerHolder(parent)
+                VIEW_TYPE_TEXT -> EditTextHolder(parent)
+                else -> throw Exception("Unknown view type")
+            }
+        }
+
+        override fun onBindViewHolder(holder: Holder, position: Int) {
+            val filter = items[position]
+            when (filter) {
+                is Filter.Header -> {
+                    val view = holder.itemView as TextView
+                    view.visibility = if (filter.name.isEmpty()) View.GONE else View.VISIBLE
+                    view.text = filter.name
+                }
+                is Filter.CheckBox -> {
+                    val view = (holder as CheckboxHolder).check
+                    view.text = filter.name
+                    view.isChecked = filter.state
+                    holder.itemView.setOnClickListener {
+                        view.toggle()
+                        filter.state = view.isChecked
+                    }
+                }
+                is Filter.TriState -> {
+                    val view = (holder as MultiStateHolder).text
+                    view.text = filter.name
+
+                    fun getIcon() = VectorDrawableCompat.create(view.resources, when (filter.state) {
+                        Filter.TriState.STATE_IGNORE -> R.drawable.ic_check_box_outline_blank_24dp
+                        Filter.TriState.STATE_INCLUDE -> R.drawable.ic_check_box_24dp
+                        Filter.TriState.STATE_EXCLUDE -> R.drawable.ic_check_box_x_24dp
+                        else -> throw Exception("Unknown state")
+                    }, null)?.apply {
+                        val color = if (filter.state == Filter.TriState.STATE_INCLUDE)
+                            R.attr.colorAccent
+                        else
+                            android.R.attr.textColorSecondary
+
+                        setTint(view.context.theme.getResourceColor(color))
+                    }
+
+                    view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
+                    holder.itemView.setOnClickListener {
+                        filter.state = (filter.state + 1) % 3
+                        view.setCompoundDrawablesWithIntrinsicBounds(getIcon(), null, null, null)
+                    }
+                }
+                is Filter.List<*> -> {
+                    holder as SpinnerHolder
+                    holder.text.text = filter.name + ": "
+
+                    val spinner = holder.spinner
+                    spinner.prompt = filter.name
+                    spinner.adapter = ArrayAdapter<Any>(holder.itemView.context,
+                            android.R.layout.simple_spinner_item, filter.values).apply {
+                        setDropDownViewResource(R.layout.spinner_item)
+                    }
+                    spinner.onItemSelectedListener = IgnoreFirstSpinnerListener { position ->
+                        filter.state = position
+                    }
+                    spinner.setSelection(filter.state)
+                }
+                is Filter.Text -> {
+                    holder as EditTextHolder
+                    holder.wrapper.visibility = if (filter.name.isEmpty()) View.GONE else View.VISIBLE
+                    holder.wrapper.hint = filter.name
+                    holder.edit.setText(filter.state)
+                    holder.edit.addTextChangedListener(object : SimpleTextWatcher() {
+                        override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
+                            filter.state = s.toString()
+                        }
+                    })
+                }
+            }
+        }
+
+    }
+
+}

+ 11 - 4
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt

@@ -65,9 +65,14 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
         private set
 
     /**
-     * Filters states.
+     * Modifiable list of filters.
      */
-    var filters: List<Filter<*>> = emptyList()
+    var sourceFilters: List<Filter<*>> = emptyList()
+
+    /**
+     * List of filters used by the [Pager]. If empty alongside [query], the popular query is used.
+     */
+    var appliedFilters: List<Filter<*>> = emptyList()
 
     /**
      * Pager containing a list of manga results.
@@ -105,6 +110,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
 
         try {
             source = getLastUsedSource()
+            sourceFilters = source.getFilterList()
         } catch (error: NoSuchElementException) {
             return
         }
@@ -130,9 +136,9 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
      * @param query the query.
      * @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.appliedFilters) {
         this.query = query
-        this.filters = filters
+        this.appliedFilters = filters
 
         if (!isListMode) {
             subscribeToMangaInitializer()
@@ -182,6 +188,7 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
     fun setActiveSource(source: OnlineSource) {
         prefs.lastUsedCatalogueSource().set(source.id)
         this.source = source
+        sourceFilters = source.getFilterList()
 
         restartPager(query = "", filters = emptyList())
     }

+ 0 - 153
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/FilterAdapter.kt

@@ -1,153 +0,0 @@
-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)
-        }
-    }
-}

+ 1 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt

@@ -39,6 +39,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
 
     init {
         recycler.adapter = adapter
+        addView(recycler)
 
         groups.forEach { it.initModels() }
     }

+ 0 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt

@@ -11,7 +11,6 @@ import eu.kanade.tachiyomi.ui.reader.ReaderChapter
 import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader
 import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.LeftToRightReader
 import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader
-import eu.kanade.tachiyomi.util.toast
 import rx.subscriptions.CompositeSubscription
 
 /**

+ 9 - 0
app/src/main/java/eu/kanade/tachiyomi/util/DimensionExtensions.kt

@@ -0,0 +1,9 @@
+package eu.kanade.tachiyomi.util
+
+import android.content.res.Resources
+
+val Int.pxToDp: Int
+    get() = (this / Resources.getSystem().displayMetrics.density).toInt()
+
+val Int.dpToPx: Int
+    get() = (this * Resources.getSystem().displayMetrics.density).toInt()

+ 6 - 128
app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt

@@ -1,88 +1,27 @@
 package eu.kanade.tachiyomi.widget
 
-import android.annotation.SuppressLint
 import android.content.Context
 import android.graphics.drawable.Drawable
 import android.support.annotation.CallSuper
-import android.support.design.R
-import android.support.design.internal.ScrimInsetsFrameLayout
 import android.support.graphics.drawable.VectorDrawableCompat
 import android.support.v4.content.ContextCompat
-import android.support.v4.view.ViewCompat
-import android.support.v7.widget.LinearLayoutManager
 import android.support.v7.widget.RecyclerView
-import android.support.v7.widget.TintTypedArray
 import android.util.AttributeSet
 import android.view.View
 import android.view.ViewGroup
-import android.widget.CheckBox
-import android.widget.CheckedTextView
-import android.widget.RadioButton
 import android.widget.TextView
+import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.util.getResourceColor
-import eu.kanade.tachiyomi.util.inflate
-import eu.kanade.tachiyomi.R as TR
 
 /**
  * An alternative implementation of [android.support.design.widget.NavigationView], without menu
  * inflation and allowing customizable items (multiple selections, custom views, etc).
  */
-@Suppress("LeakingThis")
-@SuppressLint("PrivateResource")
 open class ExtendedNavigationView @JvmOverloads constructor(
         context: Context,
         attrs: AttributeSet? = null,
         defStyleAttr: Int = 0)
-: ScrimInsetsFrameLayout(context, attrs, defStyleAttr) {
-
-    /**
-     * Max width of the navigation view.
-     */
-    private var maxWidth: Int
-
-    /**
-     * Recycler view containing all the items.
-     */
-    protected val recycler = RecyclerView(context)
-
-    init {
-        // Custom attributes
-        val a = TintTypedArray.obtainStyledAttributes(context, attrs,
-                R.styleable.NavigationView, defStyleAttr,
-                R.style.Widget_Design_NavigationView)
-
-        ViewCompat.setBackground(
-                this, a.getDrawable(R.styleable.NavigationView_android_background))
-
-        if (a.hasValue(R.styleable.NavigationView_elevation)) {
-            ViewCompat.setElevation(this, a.getDimensionPixelSize(
-                    R.styleable.NavigationView_elevation, 0).toFloat())
-        }
-
-        ViewCompat.setFitsSystemWindows(this,
-                a.getBoolean(R.styleable.NavigationView_android_fitsSystemWindows, false))
-
-        maxWidth = a.getDimensionPixelSize(R.styleable.NavigationView_android_maxWidth, 0)
-
-        a.recycle()
-
-        recycler.layoutManager = LinearLayoutManager(context)
-        addView(recycler)
-    }
-
-    /**
-     * Overriden to measure the width of the navigation view.
-     */
-    override fun onMeasure(widthSpec: Int, heightSpec: Int) {
-        val width = when (MeasureSpec.getMode(widthSpec)) {
-            MeasureSpec.AT_MOST -> MeasureSpec.makeMeasureSpec(
-                    Math.min(MeasureSpec.getSize(widthSpec), maxWidth), MeasureSpec.EXACTLY)
-            MeasureSpec.UNSPECIFIED -> MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY)
-            else -> widthSpec
-        }
-        // Let super sort out the height
-        super.onMeasure(width, heightSpec)
-    }
+: SimpleNavigationView(context, attrs, defStyleAttr) {
 
     /**
      * Every item of the nav view. Generic items must belong to this list, custom items could be
@@ -136,7 +75,7 @@ open class ExtendedNavigationView @JvmOverloads constructor(
              */
             fun tintVector(context: Context, resId: Int): Drawable {
                 return VectorDrawableCompat.create(context.resources, resId, context.theme)!!.apply {
-                    setTint(context.theme.getResourceColor(TR.attr.colorAccent))
+                    setTint(context.theme.getResourceColor(R.attr.colorAccent))
                 }
             }
         }
@@ -161,9 +100,9 @@ open class ExtendedNavigationView @JvmOverloads constructor(
 
             override fun getStateDrawable(context: Context): Drawable? {
                 return when (state) {
-                    SORT_ASC -> tintVector(context, TR.drawable.ic_keyboard_arrow_up_black_32dp)
-                    SORT_DESC -> tintVector(context, TR.drawable.ic_keyboard_arrow_down_black_32dp)
-                    SORT_NONE -> ContextCompat.getDrawable(context, TR.drawable.empty_drawable_32dp)
+                    SORT_ASC -> tintVector(context, R.drawable.ic_keyboard_arrow_up_black_32dp)
+                    SORT_DESC -> tintVector(context, R.drawable.ic_keyboard_arrow_down_black_32dp)
+                    SORT_NONE -> ContextCompat.getDrawable(context, R.drawable.empty_drawable_32dp)
                     else -> null
                 }
             }
@@ -218,59 +157,6 @@ open class ExtendedNavigationView @JvmOverloads constructor(
 
     }
 
-    /**
-     * Base view holder.
-     */
-    abstract class Holder(view: View) : RecyclerView.ViewHolder(view)
-
-    /**
-     * Separator view holder.
-     */
-    class SeparatorHolder(parent: ViewGroup)
-        : Holder(parent.inflate(R.layout.design_navigation_item_separator))
-
-    /**
-     * Header view holder.
-     */
-    class HeaderHolder(parent: ViewGroup)
-        : Holder(parent.inflate(R.layout.design_navigation_item_subheader))
-
-    /**
-     * Clickable view holder.
-     */
-    abstract class ClickableHolder(view: View, listener: View.OnClickListener?) : Holder(view) {
-        init {
-            itemView.setOnClickListener(listener)
-        }
-    }
-
-    /**
-     * Radio view holder.
-     */
-    class RadioHolder(parent: ViewGroup, listener: View.OnClickListener?)
-        : ClickableHolder(parent.inflate(TR.layout.navigation_view_radio), listener) {
-
-        val radio = itemView.findViewById(TR.id.nav_view_item) as RadioButton
-    }
-
-    /**
-     * Checkbox view holder.
-     */
-    class CheckboxHolder(parent: ViewGroup, listener: View.OnClickListener?)
-        : ClickableHolder(parent.inflate(TR.layout.navigation_view_checkbox), listener) {
-
-        val check = itemView.findViewById(TR.id.nav_view_item) as CheckBox
-    }
-
-    /**
-     * Multi state view holder.
-     */
-    class MultiStateHolder(parent: ViewGroup, listener: View.OnClickListener?)
-        : ClickableHolder(parent.inflate(TR.layout.navigation_view_checkedtext), listener) {
-
-        val text = itemView.findViewById(TR.id.nav_view_item) as CheckedTextView
-    }
-
     /**
      * Base adapter for the navigation view. It knows how to create and render every subclass of
      * [Item].
@@ -352,12 +238,4 @@ open class ExtendedNavigationView @JvmOverloads constructor(
 
     }
 
-    companion object {
-        private const val VIEW_TYPE_HEADER = 100
-        private const val VIEW_TYPE_SEPARATOR = 101
-        private const val VIEW_TYPE_RADIO = 102
-        private const val VIEW_TYPE_CHECKBOX = 103
-        private const val VIEW_TYPE_MULTISTATE = 104
-    }
-
 }

+ 152 - 0
app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt

@@ -0,0 +1,152 @@
+package eu.kanade.tachiyomi.widget
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.support.design.R
+import android.support.design.internal.ScrimInsetsFrameLayout
+import android.support.design.widget.TextInputLayout
+import android.support.v4.view.ViewCompat
+import android.support.v7.widget.LinearLayoutManager
+import android.support.v7.widget.RecyclerView
+import android.support.v7.widget.TintTypedArray
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import android.widget.*
+import eu.kanade.tachiyomi.util.inflate
+import eu.kanade.tachiyomi.R as TR
+
+@Suppress("LeakingThis")
+@SuppressLint("PrivateResource")
+open class SimpleNavigationView @JvmOverloads constructor(
+        context: Context,
+        attrs: AttributeSet? = null,
+        defStyleAttr: Int = 0)
+: ScrimInsetsFrameLayout(context, attrs, defStyleAttr) {
+
+    /**
+     * Max width of the navigation view.
+     */
+    private var maxWidth: Int
+
+    /**
+     * Recycler view containing all the items.
+     */
+    protected val recycler = RecyclerView(context)
+
+    init {
+        // Custom attributes
+        val a = TintTypedArray.obtainStyledAttributes(context, attrs,
+                R.styleable.NavigationView, defStyleAttr,
+                R.style.Widget_Design_NavigationView)
+
+        ViewCompat.setBackground(
+                this, a.getDrawable(R.styleable.NavigationView_android_background))
+
+        if (a.hasValue(R.styleable.NavigationView_elevation)) {
+            ViewCompat.setElevation(this, a.getDimensionPixelSize(
+                    R.styleable.NavigationView_elevation, 0).toFloat())
+        }
+
+        ViewCompat.setFitsSystemWindows(this,
+                a.getBoolean(R.styleable.NavigationView_android_fitsSystemWindows, false))
+
+        maxWidth = a.getDimensionPixelSize(R.styleable.NavigationView_android_maxWidth, 0)
+
+        a.recycle()
+
+        recycler.layoutManager = LinearLayoutManager(context)
+    }
+
+    /**
+     * Overriden to measure the width of the navigation view.
+     */
+    override fun onMeasure(widthSpec: Int, heightSpec: Int) {
+        val width = when (MeasureSpec.getMode(widthSpec)) {
+            MeasureSpec.AT_MOST -> MeasureSpec.makeMeasureSpec(
+                    Math.min(MeasureSpec.getSize(widthSpec), maxWidth), MeasureSpec.EXACTLY)
+            MeasureSpec.UNSPECIFIED -> MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.EXACTLY)
+            else -> widthSpec
+        }
+        // Let super sort out the height
+        super.onMeasure(width, heightSpec)
+    }
+
+    /**
+     * Base view holder.
+     */
+    abstract class Holder(view: View) : RecyclerView.ViewHolder(view)
+
+    /**
+     * Separator view holder.
+     */
+    class SeparatorHolder(parent: ViewGroup)
+        : Holder(parent.inflate(R.layout.design_navigation_item_separator))
+
+    /**
+     * Header view holder.
+     */
+    class HeaderHolder(parent: ViewGroup)
+        : Holder(parent.inflate(R.layout.design_navigation_item_subheader))
+
+    /**
+     * Clickable view holder.
+     */
+    abstract class ClickableHolder(view: View, listener: View.OnClickListener?) : Holder(view) {
+        init {
+            itemView.setOnClickListener(listener)
+        }
+    }
+
+    /**
+     * Radio view holder.
+     */
+    class RadioHolder(parent: ViewGroup, listener: View.OnClickListener?)
+        : ClickableHolder(parent.inflate(TR.layout.navigation_view_radio), listener) {
+
+        val radio = itemView.findViewById(TR.id.nav_view_item) as RadioButton
+    }
+
+    /**
+     * Checkbox view holder.
+     */
+    class CheckboxHolder(parent: ViewGroup, listener: View.OnClickListener?)
+        : ClickableHolder(parent.inflate(TR.layout.navigation_view_checkbox), listener) {
+
+        val check = itemView.findViewById(TR.id.nav_view_item) as CheckBox
+    }
+
+    /**
+     * Multi state view holder.
+     */
+    class MultiStateHolder(parent: ViewGroup, listener: View.OnClickListener?)
+        : ClickableHolder(parent.inflate(TR.layout.navigation_view_checkedtext), listener) {
+
+        val text = itemView.findViewById(TR.id.nav_view_item) as CheckedTextView
+    }
+
+    class SpinnerHolder(parent: ViewGroup, listener: OnClickListener? = null)
+        : ClickableHolder(parent.inflate(TR.layout.navigation_view_spinner), listener) {
+
+        val text = itemView.findViewById(TR.id.nav_view_item_text) as TextView
+        val spinner = itemView.findViewById(TR.id.nav_view_item) as Spinner
+    }
+
+    class EditTextHolder(parent: ViewGroup)
+        : Holder(parent.inflate(TR.layout.navigation_view_text)) {
+
+        val wrapper  = itemView.findViewById(TR.id.nav_view_item_wrapper) as TextInputLayout
+        val edit = itemView.findViewById(TR.id.nav_view_item) as EditText
+    }
+
+    protected companion object {
+        const val VIEW_TYPE_HEADER = 100
+        const val VIEW_TYPE_SEPARATOR = 101
+        const val VIEW_TYPE_RADIO = 102
+        const val VIEW_TYPE_CHECKBOX = 103
+        const val VIEW_TYPE_MULTISTATE = 104
+        const val VIEW_TYPE_TEXT = 105
+        const val VIEW_TYPE_LIST = 106
+    }
+
+}

+ 0 - 5
app/src/main/res/drawable/ic_check_box_set.xml

@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:state_checked="true" android:drawable="@drawable/ic_check_box_24dp" />
-    <item android:drawable="@drawable/ic_check_box_outline_blank_24dp" />
-</selector>

+ 9 - 0
app/src/main/res/layout/catalogue_drawer.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<eu.kanade.tachiyomi.ui.catalogue.CatalogueNavigationView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/nav_view2"
+    android:layout_width="wrap_content"
+    android:layout_height="match_parent"
+    android:layout_gravity="end"
+    android:fitsSystemWindows="false"/>
+

+ 28 - 0
app/src/main/res/layout/catalogue_drawer_content.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="8dp">
+
+        <Button
+            android:id="@+id/search_btn"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/action_search"/>
+
+        <Button
+            android:id="@+id/reset_btn"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@string/action_reset"/>
+
+    </LinearLayout>
+
+</LinearLayout>

+ 25 - 0
app/src/main/res/layout/navigation_view_spinner.xml

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?attr/listPreferredItemHeightSmall"
+    android:paddingLeft="?attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?attr/listPreferredItemPaddingRight"
+    android:background="?attr/selectableItemBackground"
+    android:focusable="true">
+
+    <TextView
+        android:id="@+id/nav_view_item_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"/>
+
+    <Spinner
+        android:id="@+id/nav_view_item"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center_vertical|start"
+        android:maxLines="1"
+        android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
+
+</LinearLayout>

+ 29 - 0
app/src/main/res/layout/navigation_view_text.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?attr/listPreferredItemHeightSmall"
+    android:paddingLeft="?attr/listPreferredItemPaddingLeft"
+    android:paddingRight="?attr/listPreferredItemPaddingRight"
+    android:background="?attr/selectableItemBackground"
+    android:focusable="true">
+
+    <android.support.design.widget.TextInputLayout
+        android:id="@+id/nav_view_item_wrapper"
+        android:layout_width="0dp"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:gravity="center_vertical|start">
+
+        <EditText
+            android:id="@+id/nav_view_item"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:maxLines="1"
+            android:textAppearance="@style/TextAppearance.AppCompat.Body2" />
+
+    </android.support.design.widget.TextInputLayout>
+
+
+
+</LinearLayout>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -69,6 +69,7 @@
     <string name="action_install">Install</string>
     <string name="action_share">Share</string>
     <string name="action_save">Save</string>
+    <string name="action_reset">Reset</string>
 
     <!-- Operations -->
     <string name="deleting">Deleting…</string>