Manage favourites categories
parent
3014ebdfd4
commit
582adae11f
@ -0,0 +1,111 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.InputType
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import kotlinx.android.synthetic.main.activity_categories.*
|
||||||
|
import moxy.ktx.moxyPresenter
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
|
import org.koitharu.kotatsu.ui.common.BaseActivity
|
||||||
|
import org.koitharu.kotatsu.ui.common.dialog.TextInputDialog
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
|
import org.koitharu.kotatsu.utils.ext.getDisplayMessage
|
||||||
|
import org.koitharu.kotatsu.utils.ext.showPopupMenu
|
||||||
|
|
||||||
|
class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<FavouriteCategory>,
|
||||||
|
FavouriteCategoriesView, View.OnClickListener {
|
||||||
|
|
||||||
|
private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter)
|
||||||
|
|
||||||
|
private lateinit var adapter: CategoriesAdapter
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_categories)
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
fab_add.imageTintList = ColorStateList.valueOf(Color.WHITE)
|
||||||
|
adapter = CategoriesAdapter(this)
|
||||||
|
recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
fab_add.setOnClickListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClick(v: View) {
|
||||||
|
when (v.id) {
|
||||||
|
R.id.fab_add -> createCategory()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onItemClick(item: FavouriteCategory, position: Int, view: View) {
|
||||||
|
view.showPopupMenu(R.menu.popup_category) {
|
||||||
|
when (it.itemId) {
|
||||||
|
R.id.action_remove -> deleteCategory(item)
|
||||||
|
R.id.action_rename -> renameCategory(item)
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCategoriesChanged(categories: List<FavouriteCategory>) {
|
||||||
|
adapter.replaceData(categories)
|
||||||
|
textView_holder.isVisible = categories.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCheckedCategoriesChanged(checkedIds: Set<Int>) = Unit
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
Snackbar.make(recyclerView, e.getDisplayMessage(resources), Snackbar.LENGTH_LONG)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deleteCategory(category: FavouriteCategory) {
|
||||||
|
AlertDialog.Builder(this)
|
||||||
|
.setMessage(getString(R.string.category_delete_confirm, category.title))
|
||||||
|
.setTitle(R.string.remove_category)
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
.setPositiveButton(R.string.remove) { _, _ ->
|
||||||
|
presenter.deleteCategory(category.id)
|
||||||
|
}.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renameCategory(category: FavouriteCategory) {
|
||||||
|
TextInputDialog.Builder(this)
|
||||||
|
.setTitle(R.string.rename)
|
||||||
|
.setText(category.title)
|
||||||
|
.setHint(R.string.enter_category_name)
|
||||||
|
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
||||||
|
.setNegativeButton(android.R.string.cancel)
|
||||||
|
.setPositiveButton(R.string.rename) { _, name ->
|
||||||
|
presenter.renameCategory(category.id, name)
|
||||||
|
}.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createCategory() {
|
||||||
|
TextInputDialog.Builder(this)
|
||||||
|
.setTitle(R.string.add_new_category)
|
||||||
|
.setHint(R.string.enter_category_name)
|
||||||
|
.setInputType(InputType.TYPE_TEXT_VARIATION_PERSON_NAME or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES)
|
||||||
|
.setNegativeButton(android.R.string.cancel)
|
||||||
|
.setPositiveButton(R.string.add) { _, name ->
|
||||||
|
presenter.createCategory(name)
|
||||||
|
}.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun newIntent(context: Context) = Intent(context, CategoriesActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,46 +1,17 @@
|
|||||||
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
||||||
|
|
||||||
import android.util.SparseBooleanArray
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Checkable
|
|
||||||
import androidx.core.util.set
|
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
import org.koitharu.kotatsu.ui.common.list.OnRecyclerItemClickListener
|
||||||
|
import org.koitharu.kotatsu.ui.main.list.favourites.categories.select.CategoryCheckableHolder
|
||||||
|
|
||||||
class CategoriesAdapter(private val listener: OnCategoryCheckListener) :
|
class CategoriesAdapter(onItemClickListener: OnRecyclerItemClickListener<FavouriteCategory>? = null) :
|
||||||
BaseRecyclerAdapter<FavouriteCategory, Boolean>() {
|
BaseRecyclerAdapter<FavouriteCategory, Unit>(onItemClickListener) {
|
||||||
|
|
||||||
private val checkedIds = SparseBooleanArray()
|
override fun onCreateViewHolder(parent: ViewGroup) = CategoryHolder(parent)
|
||||||
|
|
||||||
fun setCheckedIds(ids: Iterable<Int>) {
|
|
||||||
checkedIds.clear()
|
|
||||||
ids.forEach {
|
|
||||||
checkedIds[it] = true
|
|
||||||
}
|
|
||||||
notifyDataSetChanged()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getExtra(item: FavouriteCategory, position: Int) =
|
|
||||||
checkedIds.get(item.id.toInt(), false)
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup) =
|
|
||||||
CategoryHolder(
|
|
||||||
parent
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun onGetItemId(item: FavouriteCategory) = item.id
|
override fun onGetItemId(item: FavouriteCategory) = item.id
|
||||||
|
|
||||||
override fun onViewHolderCreated(holder: BaseViewHolder<FavouriteCategory, Boolean>) {
|
override fun getExtra(item: FavouriteCategory, position: Int) = Unit
|
||||||
super.onViewHolderCreated(holder)
|
|
||||||
holder.itemView.setOnClickListener {
|
|
||||||
if (it !is Checkable) return@setOnClickListener
|
|
||||||
it.toggle()
|
|
||||||
if (it.isChecked) {
|
|
||||||
listener.onCategoryChecked(holder.requireData())
|
|
||||||
} else {
|
|
||||||
listener.onCategoryUnchecked(holder.requireData())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,16 +1,15 @@
|
|||||||
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
||||||
|
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import kotlinx.android.synthetic.main.item_category_checkable.*
|
import kotlinx.android.synthetic.main.item_category.*
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
|
||||||
class CategoryHolder(parent: ViewGroup) :
|
class CategoryHolder(parent: ViewGroup) :
|
||||||
BaseViewHolder<FavouriteCategory, Boolean>(parent, R.layout.item_category_checkable) {
|
BaseViewHolder<FavouriteCategory, Unit>(parent, R.layout.item_category) {
|
||||||
|
|
||||||
override fun onBind(data: FavouriteCategory, extra: Boolean) {
|
override fun onBind(data: FavouriteCategory, extra: Unit) {
|
||||||
checkedTextView.text = data.title
|
textView.text = data.title
|
||||||
checkedTextView.isChecked = extra
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories.select
|
||||||
|
|
||||||
|
import android.util.SparseBooleanArray
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Checkable
|
||||||
|
import androidx.core.util.set
|
||||||
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseRecyclerAdapter
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
|
||||||
|
class CategoriesSelectAdapter(private val listener: OnCategoryCheckListener) :
|
||||||
|
BaseRecyclerAdapter<FavouriteCategory, Boolean>() {
|
||||||
|
|
||||||
|
private val checkedIds = SparseBooleanArray()
|
||||||
|
|
||||||
|
fun setCheckedIds(ids: Iterable<Int>) {
|
||||||
|
checkedIds.clear()
|
||||||
|
ids.forEach {
|
||||||
|
checkedIds[it] = true
|
||||||
|
}
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getExtra(item: FavouriteCategory, position: Int) =
|
||||||
|
checkedIds.get(item.id.toInt(), false)
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup) =
|
||||||
|
CategoryCheckableHolder(
|
||||||
|
parent
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun onGetItemId(item: FavouriteCategory) = item.id
|
||||||
|
|
||||||
|
override fun onViewHolderCreated(holder: BaseViewHolder<FavouriteCategory, Boolean>) {
|
||||||
|
super.onViewHolderCreated(holder)
|
||||||
|
holder.itemView.setOnClickListener {
|
||||||
|
if (it !is Checkable) return@setOnClickListener
|
||||||
|
it.toggle()
|
||||||
|
if (it.isChecked) {
|
||||||
|
listener.onCategoryChecked(holder.requireData())
|
||||||
|
} else {
|
||||||
|
listener.onCategoryUnchecked(holder.requireData())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories.select
|
||||||
|
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import kotlinx.android.synthetic.main.item_category_checkable.*
|
||||||
|
import org.koitharu.kotatsu.R
|
||||||
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
|
import org.koitharu.kotatsu.ui.common.list.BaseViewHolder
|
||||||
|
|
||||||
|
class CategoryCheckableHolder(parent: ViewGroup) :
|
||||||
|
BaseViewHolder<FavouriteCategory, Boolean>(parent, R.layout.item_category_checkable) {
|
||||||
|
|
||||||
|
override fun onBind(data: FavouriteCategory, extra: Boolean) {
|
||||||
|
checkedTextView.text = data.title
|
||||||
|
checkedTextView.isChecked = extra
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package org.koitharu.kotatsu.ui.main.list.favourites.categories
|
package org.koitharu.kotatsu.ui.main.list.favourites.categories.select
|
||||||
|
|
||||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||||
|
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/appbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?colorPrimary"
|
||||||
|
android:fitsSystemWindows="true"
|
||||||
|
android:theme="@style/AppToolbarTheme">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:popupTheme="@style/AppPopupTheme" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||||
|
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/textView_holder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible"
|
||||||
|
android:layout_margin="20dp"
|
||||||
|
android:textAppearance="?android:textAppearanceMedium"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:text="@string/text_categories_holder"/>
|
||||||
|
|
||||||
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
|
android:id="@+id/fab_add"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:contentDescription="@string/add_new_category"
|
||||||
|
android:src="@drawable/ic_add"
|
||||||
|
app:backgroundTint="?colorAccent"
|
||||||
|
app:fabSize="normal"
|
||||||
|
app:layout_anchor="@id/recyclerView"
|
||||||
|
app:layout_anchorGravity="bottom|end"
|
||||||
|
app:layout_dodgeInsetEdges="bottom" />
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<TextView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:id="@+id/textView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:listPreferredItemHeight"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
android:gravity="start|center_vertical"
|
||||||
|
android:paddingStart="?android:listPreferredItemPaddingStart"
|
||||||
|
android:paddingEnd="?android:listPreferredItemPaddingEnd"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:textColor="?android:textColorPrimary"
|
||||||
|
tools:text="@tools:sample/lorem[4]" />
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_categories"
|
||||||
|
android:orderInCategory="50"
|
||||||
|
android:title="@string/categories_"
|
||||||
|
app:showAsAction="never" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_remove"
|
||||||
|
android:title="@string/remove" />
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_rename"
|
||||||
|
android:title="@string/rename" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
Loading…
Reference in New Issue