diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt index c880023af..fa19cf950 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.kt +++ b/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"), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt index f0474371c..c98cd4692 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangafox.kt +++ b/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"), diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt b/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt index 83b93b6fc..b4c683883 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Mangahere.kt +++ b/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"), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt index 8ee045875..55b8c4b29 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt +++ b/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(), 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(), 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(), 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(), 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(), 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(), 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() - } - } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueNavigationView.kt new file mode 100644 index 000000000..34cce832a --- /dev/null +++ b/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>) { + adapter.items = items + adapter.notifyDataSetChanged() + } + + inner class Adapter : RecyclerView.Adapter() { + + var items: List> = 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(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() + } + }) + } + } + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index e0ec27c8e..03adb70e6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt @@ -65,9 +65,14 @@ open class CataloguePresenter : BasePresenter() { private set /** - * Filters states. + * Modifiable list of filters. */ - var filters: List> = emptyList() + var sourceFilters: List> = emptyList() + + /** + * List of filters used by the [Pager]. If empty alongside [query], the popular query is used. + */ + var appliedFilters: List> = emptyList() /** * Pager containing a list of manga results. @@ -105,6 +110,7 @@ open class CataloguePresenter : BasePresenter() { try { source = getLastUsedSource() + sourceFilters = source.getFilterList() } catch (error: NoSuchElementException) { return } @@ -130,9 +136,9 @@ open class CataloguePresenter : BasePresenter() { * @param query the query. * @param filters the current state of the filters (for search mode). */ - fun restartPager(query: String = this.query, filters: List> = this.filters) { + fun restartPager(query: String = this.query, filters: List> = this.appliedFilters) { this.query = query - this.filters = filters + this.appliedFilters = filters if (!isListMode) { subscribeToMangaInitializer() @@ -182,6 +188,7 @@ open class CataloguePresenter : BasePresenter() { fun setActiveSource(source: OnlineSource) { prefs.lastUsedCatalogueSource().set(source.id) this.source = source + sourceFilters = source.getFilterList() restartPager(query = "", filters = emptyList()) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/FilterAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/FilterAdapter.kt deleted file mode 100644 index bf83dffba..000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/FilterAdapter.kt +++ /dev/null @@ -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>) : RecyclerView.Adapter() { - 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(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) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt index 449550a5b..8c3cb176c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryNavigationView.kt +++ b/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() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt index 5e53aebed..529436eb9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt +++ b/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 /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DimensionExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/DimensionExtensions.kt new file mode 100644 index 000000000..83e191cd3 --- /dev/null +++ b/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() \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt index 16ce78595..afb033ccf 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/widget/ExtendedNavigationView.kt +++ b/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 - } - } \ No newline at end of file diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/SimpleNavigationView.kt new file mode 100644 index 000000000..40b1cfa01 --- /dev/null +++ b/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 + } + +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check_box_set.xml b/app/src/main/res/drawable/ic_check_box_set.xml deleted file mode 100644 index c2d7e4c86..000000000 --- a/app/src/main/res/drawable/ic_check_box_set.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/app/src/main/res/layout/catalogue_drawer.xml b/app/src/main/res/layout/catalogue_drawer.xml new file mode 100644 index 000000000..e348bf34d --- /dev/null +++ b/app/src/main/res/layout/catalogue_drawer.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/catalogue_drawer_content.xml b/app/src/main/res/layout/catalogue_drawer_content.xml new file mode 100644 index 000000000..62d4a9803 --- /dev/null +++ b/app/src/main/res/layout/catalogue_drawer_content.xml @@ -0,0 +1,28 @@ + + + + + +