Popup menu on favourite tabs

This commit is contained in:
Koitharu
2020-11-02 19:54:43 +02:00
parent e497781359
commit 95a4bf41d2
10 changed files with 148 additions and 58 deletions

View File

@@ -15,16 +15,21 @@ import org.koitharu.kotatsu.domain.favourites.FavouritesRepository
import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener import org.koitharu.kotatsu.domain.favourites.OnFavouritesChangeListener
import org.koitharu.kotatsu.ui.base.BaseFragment import org.koitharu.kotatsu.ui.base.BaseFragment
import org.koitharu.kotatsu.ui.list.favourites.categories.CategoriesActivity import org.koitharu.kotatsu.ui.list.favourites.categories.CategoriesActivity
import org.koitharu.kotatsu.ui.list.favourites.categories.CategoriesEditDelegate
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesPresenter import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesPresenter
import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesView import org.koitharu.kotatsu.ui.list.favourites.categories.FavouriteCategoriesView
import org.koitharu.kotatsu.utils.ext.showPopupMenu
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites), class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
FavouriteCategoriesView, FavouriteCategoriesView, OnFavouritesChangeListener, FavouritesTabLongClickListener,
OnFavouritesChangeListener { CategoriesEditDelegate.CategoriesEditCallback {
private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter) private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter)
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
CategoriesEditDelegate(requireContext(), this)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -33,7 +38,7 @@ class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val adapter = FavouritesPagerAdapter(this) val adapter = FavouritesPagerAdapter(this, this)
pager.adapter = adapter pager.adapter = adapter
TabLayoutMediator(tabs, pager, adapter).attach() TabLayoutMediator(tabs, pager, adapter).attach()
FavouritesRepository.subscribe(this) FavouritesRepository.subscribe(this)
@@ -82,6 +87,31 @@ class FavouritesContainerFragment : BaseFragment(R.layout.fragment_favourites),
presenter.loadAllCategories() presenter.loadAllCategories()
} }
override fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean {
val menuRes = if (category.id == 0L) R.menu.popup_category_empty else R.menu.popup_category
tabView.showPopupMenu(menuRes) {
when (it.itemId) {
R.id.action_remove -> editDelegate.deleteCategory(category)
R.id.action_rename -> editDelegate.renameCategory(category)
R.id.action_create -> editDelegate.createCategory()
}
true
}
return true
}
override fun onDeleteCategory(category: FavouriteCategory) {
presenter.deleteCategory(category.id)
}
override fun onRenameCategory(category: FavouriteCategory, newName: String) {
presenter.renameCategory(category.id, newName)
}
override fun onCreateCategory(name: String) {
presenter.createCategory(name)
}
companion object { companion object {
fun newInstance() = FavouritesContainerFragment() fun newInstance() = FavouritesContainerFragment()

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.ui.list.favourites package org.koitharu.kotatsu.ui.list.favourites
import android.view.View
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
@@ -8,8 +9,11 @@ import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.base.list.AdapterUpdater import org.koitharu.kotatsu.ui.base.list.AdapterUpdater
import org.koitharu.kotatsu.utils.ext.replaceWith import org.koitharu.kotatsu.utils.ext.replaceWith
class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment), class FavouritesPagerAdapter(
TabLayoutMediator.TabConfigurationStrategy { fragment: Fragment,
private val longClickListener: FavouritesTabLongClickListener
) : FragmentStateAdapter(fragment),
TabLayoutMediator.TabConfigurationStrategy, View.OnLongClickListener {
private val dataSet = ArrayList<FavouriteCategory>() private val dataSet = ArrayList<FavouriteCategory>()
@@ -23,6 +27,8 @@ class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) { override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
val item = dataSet[position] val item = dataSet[position]
tab.text = item.title tab.text = item.title
tab.view.tag = item
tab.view.setOnLongClickListener(this)
} }
fun replaceData(data: List<FavouriteCategory>) { fun replaceData(data: List<FavouriteCategory>) {
@@ -30,4 +36,9 @@ class FavouritesPagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment
dataSet.replaceWith(data) dataSet.replaceWith(data)
updater(this) updater(this)
} }
override fun onLongClick(v: View): Boolean {
val item = v.tag as? FavouriteCategory ?: return false
return longClickListener.onTabLongClick(v, item)
}
} }

View File

@@ -0,0 +1,9 @@
package org.koitharu.kotatsu.ui.list.favourites
import android.view.View
import org.koitharu.kotatsu.core.model.FavouriteCategory
fun interface FavouritesTabLongClickListener {
fun onTabLongClick(tabView: View, category: FavouriteCategory): Boolean
}

View File

@@ -5,31 +5,29 @@ import android.content.Intent
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.text.InputType
import android.view.View import android.view.View
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_categories.* import kotlinx.android.synthetic.main.activity_categories.*
import moxy.ktx.moxyPresenter import moxy.ktx.moxyPresenter
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.base.BaseActivity import org.koitharu.kotatsu.ui.base.BaseActivity
import org.koitharu.kotatsu.ui.base.dialog.TextInputDialog
import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener import org.koitharu.kotatsu.ui.base.list.OnRecyclerItemClickListener
import org.koitharu.kotatsu.utils.ext.getDisplayMessage import org.koitharu.kotatsu.utils.ext.getDisplayMessage
import org.koitharu.kotatsu.utils.ext.showPopupMenu import org.koitharu.kotatsu.utils.ext.showPopupMenu
class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<FavouriteCategory>, class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<FavouriteCategory>,
FavouriteCategoriesView, View.OnClickListener { FavouriteCategoriesView, View.OnClickListener, CategoriesEditDelegate.CategoriesEditCallback {
private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter) private val presenter by moxyPresenter(factory = ::FavouriteCategoriesPresenter)
private lateinit var adapter: CategoriesAdapter private lateinit var adapter: CategoriesAdapter
private lateinit var reorderHelper: ItemTouchHelper private lateinit var reorderHelper: ItemTouchHelper
private lateinit var editDelegate: CategoriesEditDelegate
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@@ -37,6 +35,7 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
fab_add.imageTintList = ColorStateList.valueOf(Color.WHITE) fab_add.imageTintList = ColorStateList.valueOf(Color.WHITE)
adapter = CategoriesAdapter(this) adapter = CategoriesAdapter(this)
editDelegate = CategoriesEditDelegate(this, this)
recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL)) recyclerView.addItemDecoration(DividerItemDecoration(this, RecyclerView.VERTICAL))
recyclerView.adapter = adapter recyclerView.adapter = adapter
fab_add.setOnClickListener(this) fab_add.setOnClickListener(this)
@@ -46,15 +45,15 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
override fun onClick(v: View) { override fun onClick(v: View) {
when (v.id) { when (v.id) {
R.id.fab_add -> createCategory() R.id.fab_add -> editDelegate.createCategory()
} }
} }
override fun onItemClick(item: FavouriteCategory, position: Int, view: View) { override fun onItemClick(item: FavouriteCategory, position: Int, view: View) {
view.showPopupMenu(R.menu.popup_category) { view.showPopupMenu(R.menu.popup_category) {
when (it.itemId) { when (it.itemId) {
R.id.action_remove -> deleteCategory(item) R.id.action_remove -> editDelegate.deleteCategory(item)
R.id.action_rename -> renameCategory(item) R.id.action_rename -> editDelegate.renameCategory(item)
} }
true true
} }
@@ -79,42 +78,16 @@ class CategoriesActivity : BaseActivity(), OnRecyclerItemClickListener<Favourite
.show() .show()
} }
private fun deleteCategory(category: FavouriteCategory) { override fun onDeleteCategory(category: FavouriteCategory) {
MaterialAlertDialogBuilder(this) presenter.deleteCategory(category.id)
.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) { override fun onRenameCategory(category: FavouriteCategory, newName: String) {
TextInputDialog.Builder(this) presenter.renameCategory(category.id, newName)
.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)
.setMaxLength(12, false)
.setPositiveButton(R.string.rename) { _, name ->
presenter.renameCategory(category.id, name)
}.create()
.show()
} }
private fun createCategory() { override fun onCreateCategory(name: String) {
TextInputDialog.Builder(this) presenter.createCategory(name)
.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)
.setMaxLength(12, false)
.setPositiveButton(R.string.add) { _, name ->
presenter.createCategory(name)
}.create()
.show()
} }
private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback( private inner class ReorderHelperCallback : ItemTouchHelper.SimpleCallback(

View File

@@ -0,0 +1,61 @@
package org.koitharu.kotatsu.ui.list.favourites.categories
import android.content.Context
import android.text.InputType
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.ui.base.dialog.TextInputDialog
class CategoriesEditDelegate(
private val context: Context,
private val callback: CategoriesEditCallback
) {
fun deleteCategory(category: FavouriteCategory) {
MaterialAlertDialogBuilder(context)
.setMessage(context.getString(R.string.category_delete_confirm, category.title))
.setTitle(R.string.remove_category)
.setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.remove) { _, _ ->
callback.onDeleteCategory(category)
}.create()
.show()
}
fun renameCategory(category: FavouriteCategory) {
TextInputDialog.Builder(context)
.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)
.setMaxLength(12, false)
.setPositiveButton(R.string.rename) { _, name ->
callback.onRenameCategory(category, name)
}.create()
.show()
}
fun createCategory() {
TextInputDialog.Builder(context)
.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)
.setMaxLength(12, false)
.setPositiveButton(R.string.add) { _, name ->
callback.onCreateCategory(name)
}.create()
.show()
}
interface CategoriesEditCallback {
fun onDeleteCategory(category: FavouriteCategory)
fun onRenameCategory(category: FavouriteCategory, newName: String)
fun onCreateCategory(name: String)
}
}

View File

@@ -45,7 +45,6 @@ class FavouriteCategoriesPresenter : BasePresenter<FavouriteCategoriesView>() {
fun renameCategory(id: Long, name: String) { fun renameCategory(id: Long, name: String) {
launchJob { launchJob {
repository.renameCategory(id, name) repository.renameCategory(id, name)
} }
} }

View File

@@ -3,7 +3,10 @@ package org.koitharu.kotatsu.utils.ext
import android.app.Activity import android.app.Activity
import android.graphics.Rect import android.graphics.Rect
import android.util.Log import android.util.Log
import android.view.* import android.view.LayoutInflater
import android.view.Menu
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.TextView import android.widget.TextView
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
@@ -14,7 +17,6 @@ import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.postDelayed import androidx.core.view.postDelayed
import androidx.drawerlayout.widget.DrawerLayout import androidx.drawerlayout.widget.DrawerLayout
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2
@@ -35,13 +37,6 @@ fun View.showKeyboard() {
inline fun <reified T : View> ViewGroup.inflate(@LayoutRes resId: Int) = inline fun <reified T : View> ViewGroup.inflate(@LayoutRes resId: Int) =
LayoutInflater.from(context).inflate(resId, this, false) as T LayoutInflater.from(context).inflate(resId, this, false) as T
fun RecyclerView.lookupSpanSize(callback: (Int) -> Int) {
(layoutManager as? GridLayoutManager)?.spanSizeLookup =
object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int) = callback(position)
}
}
val RecyclerView.hasItems: Boolean val RecyclerView.hasItems: Boolean
get() = (adapter?.itemCount ?: 0) > 0 get() = (adapter?.itemCount ?: 0) > 0
@@ -82,14 +77,15 @@ fun View.disableFor(timeInMillis: Long) {
} }
} }
fun View.showPopupMenu( inline fun View.showPopupMenu(
@MenuRes menuRes: Int, onPrepare: ((Menu) -> Unit)? = null, @MenuRes menuRes: Int,
onItemClick: (MenuItem) -> Boolean onPrepare: (Menu) -> Unit = {},
onItemClick: PopupMenu.OnMenuItemClickListener
) { ) {
val menu = PopupMenu(context, this) val menu = PopupMenu(context, this)
menu.inflate(menuRes) menu.inflate(menuRes)
menu.setOnMenuItemClickListener(onItemClick) menu.setOnMenuItemClickListener(onItemClick)
onPrepare?.invoke(menu.menu) onPrepare(menu.menu)
menu.show() menu.show()
} }

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_create"
android:title="@string/create_category" />
</menu>

View File

@@ -162,4 +162,5 @@
<string name="right_to_left">Справа налево</string> <string name="right_to_left">Справа налево</string>
<string name="prefer_rtl_reader">Предпочитать режим Справа налево</string> <string name="prefer_rtl_reader">Предпочитать режим Справа налево</string>
<string name="prefer_rtl_reader_summary">Вы можете настроить режим чтения для каждой манги отдельно</string> <string name="prefer_rtl_reader_summary">Вы можете настроить режим чтения для каждой манги отдельно</string>
<string name="create_category">Создать категорию</string>
</resources> </resources>

View File

@@ -163,4 +163,5 @@
<string name="right_to_left">Right to left</string> <string name="right_to_left">Right to left</string>
<string name="prefer_rtl_reader">Prefer Right to left reader</string> <string name="prefer_rtl_reader">Prefer Right to left reader</string>
<string name="prefer_rtl_reader_summary">You can set up the reading mode for each manga separately</string> <string name="prefer_rtl_reader_summary">You can set up the reading mode for each manga separately</string>
<string name="create_category">New category</string>
</resources> </resources>