mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-09 05:25:08 +01:00
Changing Add to/Change in library dialog to a bottom sheet
I just don't get tired of them. This also has a button to add a new category Also updating the snackbar when adding to library to show a "change" button to update the categories. In case you have a default category set
This commit is contained in:
parent
6d6766a86a
commit
0e62516777
@ -78,19 +78,19 @@ interface Category : Serializable {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DRAG_AND_DROP = 'D'
|
||||
private const val ALPHA_ASC = 'a'
|
||||
private const val ALPHA_DSC = 'b'
|
||||
private const val UPDATED_ASC = 'c'
|
||||
private const val UPDATED_DSC = 'd'
|
||||
private const val UNREAD_ASC = 'e'
|
||||
private const val UNREAD_DSC = 'f'
|
||||
private const val LAST_READ_ASC = 'g'
|
||||
private const val LAST_READ_DSC = 'h'
|
||||
private const val TOTAL_ASC = 'i'
|
||||
private const val TOTAL_DSC = 'j'
|
||||
private const val DATE_ADDED_ASC = 'k'
|
||||
private const val DATE_ADDED_DSC = 'l'
|
||||
const val DRAG_AND_DROP = 'D'
|
||||
const val ALPHA_ASC = 'a'
|
||||
const val ALPHA_DSC = 'b'
|
||||
const val UPDATED_ASC = 'c'
|
||||
const val UPDATED_DSC = 'd'
|
||||
const val UNREAD_ASC = 'e'
|
||||
const val UNREAD_DSC = 'f'
|
||||
const val LAST_READ_ASC = 'g'
|
||||
const val LAST_READ_DSC = 'h'
|
||||
const val TOTAL_ASC = 'i'
|
||||
const val TOTAL_DSC = 'j'
|
||||
const val DATE_ADDED_ASC = 'k'
|
||||
const val DATE_ADDED_DSC = 'l'
|
||||
|
||||
fun create(name: String): Category = CategoryImpl().apply {
|
||||
this.name = name
|
||||
|
@ -72,10 +72,10 @@ class CategoryPresenter(
|
||||
val cat = Category.create(name)
|
||||
|
||||
// Set the new item in the last position.
|
||||
cat.order = categories.map { it.order + 1 }.max() ?: 0
|
||||
cat.order = categories.maxOf { it.order } + 1
|
||||
|
||||
// Insert into database.
|
||||
cat.mangaSort = 'a'
|
||||
cat.mangaSort = Category.ALPHA_ASC
|
||||
db.insertCategory(cat).executeAsBlocking()
|
||||
val cats = db.getCategories().executeAsBlocking()
|
||||
val newCat = cats.find { it.name == name } ?: return false
|
||||
|
@ -1,9 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.widget.CompoundButton
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.addTextChangedListener
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.callbacks.onShow
|
||||
import com.afollestad.materialdialogs.customview.customView
|
||||
import com.afollestad.materialdialogs.customview.getCustomView
|
||||
import com.tfcporciuncula.flow.Preference
|
||||
@ -15,8 +19,6 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.databinding.MangaCategoryDialogBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||
@ -26,12 +28,12 @@ import uy.kohesive.injekt.injectLazy
|
||||
class ManageCategoryDialog(bundle: Bundle? = null) :
|
||||
DialogController(bundle) {
|
||||
|
||||
constructor(libraryController: LibraryController, category: Category) : this() {
|
||||
this.libraryController = libraryController
|
||||
constructor(category: Category?, updateLibrary: ((Int?) -> Unit)) : this() {
|
||||
this.updateLibrary = updateLibrary
|
||||
this.category = category
|
||||
}
|
||||
|
||||
private var libraryController: LibraryController? = null
|
||||
private var updateLibrary: ((Int?) -> Unit)? = null
|
||||
private var category: Category? = null
|
||||
|
||||
private val preferences by injectLazy<PreferencesHelper>()
|
||||
@ -39,28 +41,59 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
||||
lateinit var binding: MangaCategoryDialogBinding
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val dialog = MaterialDialog(activity!!).apply {
|
||||
title(R.string.manage_category)
|
||||
customView(viewRes = R.layout.manga_category_dialog)
|
||||
negativeButton(android.R.string.cancel)
|
||||
positiveButton(R.string.save) { onPositiveButtonClick() }
|
||||
}
|
||||
val dialog = dialog(activity!!)
|
||||
binding = MangaCategoryDialogBinding.bind(dialog.getCustomView())
|
||||
onViewCreated()
|
||||
return dialog
|
||||
}
|
||||
|
||||
private fun onPositiveButtonClick() {
|
||||
val category = category ?: return
|
||||
if (category.id ?: 0 <= 0) return
|
||||
fun dialog(activity: Activity): MaterialDialog {
|
||||
return MaterialDialog(activity).apply {
|
||||
title(if (category == null) R.string.new_category else R.string.manage_category)
|
||||
customView(viewRes = R.layout.manga_category_dialog)
|
||||
negativeButton(android.R.string.cancel) { dismiss() }
|
||||
positiveButton(R.string.save) {
|
||||
if (onPositiveButtonClick()) {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
noAutoDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(activity: Activity) {
|
||||
val dialog = dialog(activity)
|
||||
binding = MangaCategoryDialogBinding.bind(dialog.getCustomView())
|
||||
onViewCreated()
|
||||
dialog.onShow {
|
||||
binding.title.requestFocus()
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun onPositiveButtonClick(): Boolean {
|
||||
if (category?.id ?: 0 <= 0 && category != null) return false
|
||||
val text = binding.title.text.toString()
|
||||
val categoryExists = categoryExists(text)
|
||||
if (text.isNotBlank() && !categoryExists && !text.equals(category.name, true)) {
|
||||
val category = this.category ?: Category.create(text)
|
||||
if (text.isNotBlank() && !categoryExists && !text.equals(this.category?.name ?: "", true)) {
|
||||
category.name = text
|
||||
db.insertCategory(category).executeAsBlocking()
|
||||
libraryController?.presenter?.getLibrary()
|
||||
if (this.category == null) {
|
||||
val categories = db.getCategories().executeAsBlocking()
|
||||
category.order = categories.maxOf { it.order } + 1
|
||||
category.mangaSort = Category.ALPHA_ASC
|
||||
val dbCategory = db.insertCategory(category).executeAsBlocking()
|
||||
category.id = dbCategory.insertedId()?.toInt()
|
||||
this.category = category
|
||||
} else {
|
||||
db.insertCategory(category).executeAsBlocking()
|
||||
}
|
||||
} else if (categoryExists) {
|
||||
activity?.toast(R.string.category_with_name_exists)
|
||||
binding.categoryTextLayout.error = binding.categoryTextLayout.context.getString(R.string.category_with_name_exists)
|
||||
return false
|
||||
} else {
|
||||
binding.categoryTextLayout.error = binding.categoryTextLayout.context.getString(R.string.category_cannot_be_blank)
|
||||
return false
|
||||
}
|
||||
if (!updatePref(preferences.downloadNewCategories(), binding.downloadNew)) {
|
||||
preferences.downloadNew().set(false)
|
||||
@ -73,6 +106,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
||||
preferences.libraryUpdateInterval().set(0)
|
||||
LibraryUpdateJob.setupTask(0)
|
||||
}
|
||||
updateLibrary?.invoke(category.id)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
@ -85,19 +120,22 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
||||
}
|
||||
|
||||
fun onViewCreated() {
|
||||
val category = category ?: return
|
||||
if (category.id ?: 0 <= 0) {
|
||||
if (category?.id ?: 0 <= 0 && category != null) {
|
||||
binding.title.gone()
|
||||
binding.downloadNew.gone()
|
||||
binding.includeGlobal.gone()
|
||||
return
|
||||
}
|
||||
binding.editCategories.isVisible = category != null
|
||||
binding.editCategories.setOnClickListener {
|
||||
router.popCurrentController()
|
||||
router.pushController(CategoryController().withFadeTransaction())
|
||||
}
|
||||
binding.title.hint = category.name
|
||||
binding.title.append(category.name)
|
||||
binding.title.addTextChangedListener {
|
||||
binding.categoryTextLayout.error = null
|
||||
}
|
||||
binding.title.hint = category?.name ?: binding.editCategories.context.getString(R.string.category)
|
||||
binding.title.append(category?.name ?: "")
|
||||
val downloadNew = preferences.downloadNew().get()
|
||||
setCheckbox(
|
||||
binding.downloadNew,
|
||||
|
@ -0,0 +1,37 @@
|
||||
package eu.kanade.tachiyomi.ui.category.addtolibrary
|
||||
|
||||
import android.view.View
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.items.AbstractItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.databinding.AddCategoryItemBinding
|
||||
|
||||
class AddCategoryItem(val category: Category) : AbstractItem<FastAdapter.ViewHolder<AddCategoryItem>>() {
|
||||
|
||||
/** defines the type defining this item. must be unique. preferably an id */
|
||||
override val type: Int = R.id.category_checkbox
|
||||
|
||||
/** defines the layout which will be used for this item in the list */
|
||||
override val layoutRes: Int = R.layout.add_category_item
|
||||
|
||||
override var identifier = category.id?.toLong() ?: -1L
|
||||
|
||||
override fun getViewHolder(v: View): FastAdapter.ViewHolder<AddCategoryItem> {
|
||||
return ViewHolder(v)
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : FastAdapter.ViewHolder<AddCategoryItem>(view) {
|
||||
|
||||
val binding = AddCategoryItemBinding.bind(view)
|
||||
override fun bindView(item: AddCategoryItem, payloads: List<Any>) {
|
||||
binding.categoryCheckbox.text = item.category.name
|
||||
binding.categoryCheckbox.isChecked = item.isSelected
|
||||
}
|
||||
|
||||
override fun unbindView(item: AddCategoryItem) {
|
||||
binding.categoryCheckbox.text = null
|
||||
binding.categoryCheckbox.isChecked = false
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
package eu.kanade.tachiyomi.ui.category.addtolibrary
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.ISelectionListener
|
||||
import com.mikepenz.fastadapter.adapters.ItemAdapter
|
||||
import com.mikepenz.fastadapter.select.SelectExtension
|
||||
import com.mikepenz.fastadapter.select.getSelectExtension
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.databinding.SetCategoriesSheetBinding
|
||||
import eu.kanade.tachiyomi.ui.category.ManageCategoryDialog
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.view.expand
|
||||
import eu.kanade.tachiyomi.util.view.setEdgeToEdge
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
|
||||
class SetCategoriesSheet(
|
||||
private val activity: Activity,
|
||||
private val manga: Manga,
|
||||
var categories: MutableList<Category>,
|
||||
var preselected: Array<Int>,
|
||||
val addingToLibrary: Boolean,
|
||||
val onMangaAdded: (() -> Unit) = { }
|
||||
) : BottomSheetDialog
|
||||
(activity, R.style.BottomSheetDialogTheme) {
|
||||
|
||||
private var sheetBehavior: BottomSheetBehavior<*>
|
||||
|
||||
private val fastAdapter: FastAdapter<AddCategoryItem>
|
||||
private val itemAdapter = ItemAdapter<AddCategoryItem>()
|
||||
private val selectExtension: SelectExtension<AddCategoryItem>
|
||||
private val db: DatabaseHelper by injectLazy()
|
||||
|
||||
private val binding = SetCategoriesSheetBinding.inflate(activity.layoutInflater)
|
||||
init {
|
||||
// Use activity theme for this layout
|
||||
setContentView(binding.root)
|
||||
|
||||
sheetBehavior = BottomSheetBehavior.from(binding.root.parent as ViewGroup)
|
||||
setEdgeToEdge(activity, binding.root)
|
||||
|
||||
binding.toolbarTitle.text = context.getString(
|
||||
if (addingToLibrary) {
|
||||
R.string.add_x_to
|
||||
} else {
|
||||
R.string.move_x_to
|
||||
},
|
||||
manga.mangaType(context)
|
||||
)
|
||||
|
||||
setOnShowListener {
|
||||
updateBottomButtons()
|
||||
}
|
||||
sheetBehavior.addBottomSheetCallback(
|
||||
object : BottomSheetBehavior.BottomSheetCallback() {
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
updateBottomButtons()
|
||||
}
|
||||
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
updateBottomButtons()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
binding.categoryRecyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
sheetBehavior.isDraggable = !recyclerView.canScrollVertically(-1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (recyclerView.canScrollVertically(-1)) {
|
||||
sheetBehavior.isDraggable = false
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
binding.titleLayout.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
binding.categoryRecyclerView.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
val fullHeight = activity.window.decorView.height
|
||||
val insets = activity.window.decorView.rootWindowInsets
|
||||
matchConstraintMaxHeight =
|
||||
fullHeight - (insets?.systemWindowInsetTop ?: 0) -
|
||||
binding.titleLayout.height - binding.buttonLayout.height - 75.dpToPx
|
||||
}
|
||||
}
|
||||
|
||||
fastAdapter = FastAdapter.with(itemAdapter)
|
||||
fastAdapter.setHasStableIds(true)
|
||||
binding.categoryRecyclerView.layoutManager = LinearLayoutManager(context)
|
||||
binding.categoryRecyclerView.adapter = fastAdapter
|
||||
itemAdapter.set(categories.map(::AddCategoryItem))
|
||||
itemAdapter.adapterItems.forEach { item ->
|
||||
item.isSelected = preselected.any { it == item.category.id }
|
||||
}
|
||||
|
||||
selectExtension = fastAdapter.getSelectExtension()
|
||||
selectExtension.apply {
|
||||
isSelectable = true
|
||||
multiSelect = true
|
||||
setCategoriesButtons()
|
||||
selectionListener = object : ISelectionListener<AddCategoryItem> {
|
||||
override fun onSelectionChanged(item: AddCategoryItem, selected: Boolean) {
|
||||
setCategoriesButtons()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setCategoriesButtons() {
|
||||
binding.addToCategoriesButton.text = context.getString(
|
||||
if (addingToLibrary) {
|
||||
R.string.add_to_
|
||||
} else {
|
||||
R.string.move_to_
|
||||
},
|
||||
when (selectExtension.selections.size) {
|
||||
0 -> context.getString(R.string.default_category).lowercase(Locale.ROOT)
|
||||
1 -> selectExtension.selectedItems.firstOrNull()?.category?.name ?: ""
|
||||
else -> context.resources.getQuantityString(
|
||||
R.plurals.category_plural,
|
||||
selectExtension.selections.size,
|
||||
selectExtension.selections.size
|
||||
)
|
||||
}
|
||||
)
|
||||
binding.categoryRecyclerView.scrollToPosition(
|
||||
max(0, itemAdapter.adapterItems.indexOf(selectExtension.selectedItems.firstOrNull()))
|
||||
)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
sheetBehavior.expand()
|
||||
sheetBehavior.skipCollapsed = true
|
||||
updateBottomButtons()
|
||||
}
|
||||
|
||||
fun updateBottomButtons() {
|
||||
val bottomSheet = binding.root.parent as View
|
||||
val bottomSheetVisibleHeight = -bottomSheet.top + (activity.window.decorView.height - bottomSheet.height)
|
||||
|
||||
binding.buttonLayout.translationY = bottomSheetVisibleHeight.toFloat()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val attrsArray = intArrayOf(android.R.attr.actionBarSize)
|
||||
val array = context.obtainStyledAttributes(attrsArray)
|
||||
val headerHeight = array.getDimensionPixelSize(0, 0)
|
||||
binding.buttonLayout.updatePaddingRelative(
|
||||
bottom = activity.window.decorView.rootWindowInsets.systemWindowInsetBottom
|
||||
)
|
||||
|
||||
binding.buttonLayout.updateLayoutParams<ConstraintLayout.LayoutParams> {
|
||||
height = headerHeight + binding.buttonLayout.paddingBottom
|
||||
}
|
||||
array.recycle()
|
||||
|
||||
binding.cancelButton.setOnClickListener { dismiss() }
|
||||
binding.newCategoryButton.setOnClickListener {
|
||||
ManageCategoryDialog(null) {
|
||||
categories = db.getCategories().executeAsBlocking()
|
||||
itemAdapter.set(categories.map(::AddCategoryItem))
|
||||
itemAdapter.adapterItems.forEach { item ->
|
||||
item.isSelected = it == item.category.id
|
||||
}
|
||||
setCategoriesButtons()
|
||||
}.show(activity)
|
||||
}
|
||||
|
||||
binding.addToCategoriesButton.setOnClickListener {
|
||||
addMangaToCategories()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addMangaToCategories() {
|
||||
if (!manga.favorite) {
|
||||
manga.favorite = !manga.favorite
|
||||
|
||||
manga.date_added = Date().time
|
||||
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
val selectedCategories = selectExtension.selectedItems.map(AddCategoryItem::category)
|
||||
val mc = selectedCategories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mc, listOf(manga))
|
||||
onMangaAdded()
|
||||
}
|
||||
}
|
@ -1,66 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.afollestad.materialdialogs.callbacks.onCancel
|
||||
import com.afollestad.materialdialogs.list.listItemsMultiChoice
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
|
||||
/**
|
||||
* This class is used when adding new manga to your library
|
||||
*/
|
||||
class AddToLibraryCategoriesDialog<T>(bundle: Bundle? = null) :
|
||||
DialogController(bundle) where T : Controller, T : AddToLibraryCategoriesDialog.Listener {
|
||||
|
||||
private var manga: Manga? = null
|
||||
|
||||
private var categories = emptyList<Category>()
|
||||
|
||||
private var preselected = emptyArray<Int>()
|
||||
|
||||
private var position = 0
|
||||
|
||||
constructor(
|
||||
target: T,
|
||||
manga: Manga,
|
||||
categories: List<Category>,
|
||||
preselected: Array<Int>,
|
||||
position: Int = 0
|
||||
) : this() {
|
||||
|
||||
this.manga = manga
|
||||
this.categories = categories
|
||||
this.preselected = preselected
|
||||
this.position = position
|
||||
targetController = target
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialDialog(activity!!).title(R.string.add_to_library).message(R.string.add_to_categories)
|
||||
.listItemsMultiChoice(
|
||||
items = categories.map { it.name },
|
||||
initialSelection = preselected.toIntArray(),
|
||||
allowEmptySelection = true
|
||||
) { _, selections, _ ->
|
||||
val newCategories = selections.map { categories[it] }
|
||||
(targetController as? Listener)?.updateCategoriesForManga(manga, newCategories)
|
||||
}
|
||||
.positiveButton(android.R.string.ok)
|
||||
.negativeButton(android.R.string.cancel) {
|
||||
(targetController as? Listener)?.addToLibraryCancelled(manga, position)
|
||||
}
|
||||
.onCancel {
|
||||
(targetController as? Listener)?.addToLibraryCancelled(manga, position)
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun updateCategoriesForManga(manga: Manga?, categories: List<Category>)
|
||||
fun addToLibraryCancelled(manga: Manga?, position: Int)
|
||||
}
|
||||
}
|
@ -1332,7 +1332,9 @@ class LibraryController(
|
||||
override fun manageCategory(position: Int) {
|
||||
val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return
|
||||
if (!category.isDynamic) {
|
||||
ManageCategoryDialog(this, category).showDialog(router)
|
||||
ManageCategoryDialog(category) {
|
||||
presenter.getLibrary()
|
||||
}.showDialog(router)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,8 +68,6 @@ import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.ui.library.AddToLibraryCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.library.ChangeMangaCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
||||
@ -84,6 +82,8 @@ import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.ui.source.BrowseController
|
||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
||||
import eu.kanade.tachiyomi.util.moveCategories
|
||||
import eu.kanade.tachiyomi.util.storage.getUriCompat
|
||||
import eu.kanade.tachiyomi.util.system.ThemeUtil
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
@ -116,9 +116,7 @@ class MangaDetailsController :
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
ActionMode.Callback,
|
||||
MangaDetailsAdapter.MangaDetailsInterface,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
ChangeMangaCategoriesDialog.Listener,
|
||||
AddToLibraryCategoriesDialog.Listener {
|
||||
FlexibleAdapter.OnItemMoveListener {
|
||||
|
||||
constructor(
|
||||
manga: Manga?,
|
||||
@ -1092,7 +1090,7 @@ class MangaDetailsController :
|
||||
popupView.setOnTouchListener(popup.dragToOpenListener)
|
||||
}
|
||||
|
||||
fun makeFavPopup(popupView: View, manga: Manga, categories: List<Category>): PopupMenu {
|
||||
private fun makeFavPopup(popupView: View, manga: Manga, categories: List<Category>): PopupMenu {
|
||||
val popup = PopupMenu(view!!.context, popupView)
|
||||
popup.menu.add(0, 1, 0, R.string.remove_from_library)
|
||||
if (categories.isNotEmpty()) {
|
||||
@ -1102,18 +1100,9 @@ class MangaDetailsController :
|
||||
// Set a listener so we are notified if a menu item is clicked
|
||||
popup.setOnMenuItemClickListener { menuItem ->
|
||||
if (menuItem.itemId == 0) {
|
||||
val ids = presenter.getMangaCategoryIds()
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
ChangeMangaCategoriesDialog(
|
||||
this,
|
||||
listOf(manga),
|
||||
categories,
|
||||
preselected
|
||||
).showDialog(
|
||||
router
|
||||
)
|
||||
presenter.manga.moveCategories(presenter.db, activity!!) {
|
||||
updateHeader()
|
||||
}
|
||||
} else {
|
||||
toggleMangaFavorite()
|
||||
}
|
||||
@ -1123,31 +1112,24 @@ class MangaDetailsController :
|
||||
}
|
||||
|
||||
private fun toggleMangaFavorite() {
|
||||
if (presenter.toggleFavorite()) {
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = presenter.preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
when {
|
||||
defaultCategory != null -> presenter.moveMangaToCategory(defaultCategory)
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
|
||||
presenter.moveMangaToCategory(null)
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds()
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
|
||||
AddToLibraryCategoriesDialog(
|
||||
this,
|
||||
presenter.manga,
|
||||
categories,
|
||||
preselected
|
||||
).showDialog(router)
|
||||
}
|
||||
}
|
||||
showAddedSnack()
|
||||
} else {
|
||||
showRemovedSnack()
|
||||
val view = view ?: return
|
||||
val activity = activity ?: return
|
||||
snack?.dismiss()
|
||||
snack = presenter.manga.addOrRemoveToFavorites(
|
||||
presenter.db,
|
||||
presenter.preferences,
|
||||
view,
|
||||
activity,
|
||||
onMangaAdded = {
|
||||
updateHeader()
|
||||
showAddedSnack()
|
||||
},
|
||||
onMangaMoved = { updateHeader() },
|
||||
onMangaDeleted = { presenter.confirmDeletion() }
|
||||
)
|
||||
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
|
||||
val favButton = getHeader()?.binding?.favoriteButton
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack, favButton)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1157,43 +1139,8 @@ class MangaDetailsController :
|
||||
snack = view.snack(view.context.getString(R.string.added_to_library))
|
||||
}
|
||||
|
||||
private fun showRemovedSnack() {
|
||||
val view = view ?: return
|
||||
snack?.dismiss()
|
||||
snack = view.snack(
|
||||
view.context.getString(R.string.removed_from_library),
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
) {
|
||||
setAction(R.string.undo) {
|
||||
presenter.setFavorite(true)
|
||||
}
|
||||
addCallback(
|
||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!presenter.manga.favorite) presenter.confirmDeletion()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
val favButton = getHeader()?.binding?.favoriteButton
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack, favButton)
|
||||
}
|
||||
|
||||
override fun mangaPresenter(): MangaDetailsPresenter = presenter
|
||||
|
||||
override fun updateCategoriesForMangas(mangas: List<Manga>, categories: List<Category>) {
|
||||
presenter.moveMangaToCategories(categories)
|
||||
}
|
||||
|
||||
override fun updateCategoriesForManga(manga: Manga?, categories: List<Category>) {
|
||||
manga?.let { presenter.moveMangaToCategories(categories) }
|
||||
}
|
||||
|
||||
override fun addToLibraryCancelled(manga: Manga?, position: Int) {
|
||||
manga?.let { presenter.toggleFavorite() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a string to clipboard
|
||||
*
|
||||
|
@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.database.models.Track
|
||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
@ -61,7 +60,7 @@ class MangaDetailsPresenter(
|
||||
val source: Source,
|
||||
val preferences: PreferencesHelper = Injekt.get(),
|
||||
val coverCache: CoverCache = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
private val downloadManager: DownloadManager = Injekt.get(),
|
||||
private val chapterFilter: ChapterFilter = Injekt.get()
|
||||
) : DownloadQueue.DownloadListener, LibraryServiceListener {
|
||||
@ -334,7 +333,6 @@ class MangaDetailsPresenter(
|
||||
}
|
||||
|
||||
val networkManga = nManga.await()
|
||||
val mangaWasInitalized = manga.initialized
|
||||
if (networkManga != null) {
|
||||
manga.copyFrom(networkManga)
|
||||
manga.initialized = true
|
||||
@ -405,7 +403,7 @@ class MangaDetailsPresenter(
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
|
||||
return@launch
|
||||
} ?: listOf()
|
||||
}
|
||||
isLoading = false
|
||||
try {
|
||||
syncChaptersWithSource(db, chapters, manga, source)
|
||||
@ -551,38 +549,6 @@ class MangaDetailsPresenter(
|
||||
return db.getCategories().executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to the category.
|
||||
*
|
||||
* @param manga the manga to move.
|
||||
* @param category the selected category, or null for default category.
|
||||
*/
|
||||
fun moveMangaToCategory(category: Category?) {
|
||||
moveMangaToCategories(listOfNotNull(category))
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to categories.
|
||||
*
|
||||
* @param manga the manga to move.
|
||||
* @param categories the selected categories.
|
||||
*/
|
||||
fun moveMangaToCategories(categories: List<Category>) {
|
||||
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mc, listOf(manga))
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
|
||||
*
|
||||
* @param manga the manga to get categories from.
|
||||
* @return Array of category ids the manga is in, if none returns default id
|
||||
*/
|
||||
fun getMangaCategoryIds(): Array<Int> {
|
||||
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
|
||||
return categories.mapNotNull { it.id }.toTypedArray()
|
||||
}
|
||||
|
||||
fun confirmDeletion() {
|
||||
coverCache.deleteFromCache(manga)
|
||||
db.resetMangaInfo(manga).executeAsBlocking()
|
||||
|
@ -11,13 +11,11 @@ import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
@ -28,11 +26,11 @@ import eu.kanade.tachiyomi.source.model.Filter
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.library.AddToLibraryCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||
import eu.kanade.tachiyomi.ui.source.BrowseController
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
|
||||
import eu.kanade.tachiyomi.util.system.connectivityManager
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
@ -60,8 +58,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
NucleusController<BrowseSourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.EndlessScrollListener,
|
||||
AddToLibraryCategoriesDialog.Listener {
|
||||
FlexibleAdapter.EndlessScrollListener {
|
||||
|
||||
constructor(
|
||||
source: CatalogueSource,
|
||||
@ -575,73 +572,23 @@ open class BrowseSourceController(bundle: Bundle) :
|
||||
*/
|
||||
override fun onItemLongClick(position: Int) {
|
||||
val manga = (adapter?.getItem(position) as? BrowseSourceItem?)?.manga ?: return
|
||||
val view = view ?: return
|
||||
val activity = activity ?: return
|
||||
snack?.dismiss()
|
||||
if (manga.favorite) {
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
snack = binding.sourceLayout.snack(R.string.removed_from_library, Snackbar.LENGTH_INDEFINITE) {
|
||||
setAction(R.string.undo) {
|
||||
if (!manga.favorite) addManga(manga, position)
|
||||
}
|
||||
addCallback(
|
||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!manga.favorite) presenter.confirmDeletion(manga)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
snack = manga.addOrRemoveToFavorites(
|
||||
presenter.db,
|
||||
preferences,
|
||||
view,
|
||||
activity,
|
||||
onMangaAdded = {
|
||||
adapter?.notifyItemChanged(position)
|
||||
snack = view.snack(R.string.added_to_library)
|
||||
},
|
||||
onMangaMoved = { adapter?.notifyItemChanged(position) },
|
||||
onMangaDeleted = { presenter.confirmDeletion(manga) }
|
||||
)
|
||||
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
||||
} else {
|
||||
addManga(manga, position)
|
||||
snack = binding.sourceLayout.snack(R.string.added_to_library)
|
||||
}
|
||||
}
|
||||
|
||||
private fun addManga(manga: Manga, position: Int) {
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
|
||||
val categories = presenter.getCategories()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
when {
|
||||
defaultCategory != null -> presenter.moveMangaToCategory(manga, defaultCategory)
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
|
||||
presenter.moveMangaToCategory(manga, null)
|
||||
else -> {
|
||||
val ids = presenter.getMangaCategoryIds(manga)
|
||||
if (ids.isNullOrEmpty()) {
|
||||
presenter.moveMangaToCategory(manga, null)
|
||||
}
|
||||
val preselected = ids.mapNotNull { id ->
|
||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
||||
}.toTypedArray()
|
||||
|
||||
AddToLibraryCategoriesDialog(this, manga, categories, preselected, position)
|
||||
.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update manga to use selected categories.
|
||||
*
|
||||
* @param manga The manga to move to categories.
|
||||
* @param categories The list of categories where manga will be placed.
|
||||
*/
|
||||
override fun updateCategoriesForManga(manga: Manga?, categories: List<Category>) {
|
||||
manga?.let { presenter.updateMangaCategories(manga, categories) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Update manga to remove from favorites
|
||||
*/
|
||||
override fun addToLibraryCancelled(manga: Manga?, position: Int) {
|
||||
manga?.let {
|
||||
presenter.changeMangaFavorite(manga)
|
||||
adapter?.notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,6 @@ import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||
@ -37,7 +36,6 @@ import rx.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import java.util.Date
|
||||
|
||||
/**
|
||||
* Presenter of [BrowseSourceController].
|
||||
@ -45,7 +43,7 @@ import java.util.Date
|
||||
open class BrowseSourcePresenter(
|
||||
sourceId: Long,
|
||||
sourceManager: SourceManager = Injekt.get(),
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
val db: DatabaseHelper = Injekt.get(),
|
||||
private val prefs: PreferencesHelper = Injekt.get(),
|
||||
private val coverCache: CoverCache = Injekt.get()
|
||||
) : BasePresenter<BrowseSourceController>() {
|
||||
@ -278,22 +276,6 @@ open class BrowseSourcePresenter(
|
||||
.onErrorResumeNext { Observable.just(manga) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes a manga from the library.
|
||||
*
|
||||
* @param manga the manga to update.
|
||||
*/
|
||||
fun changeMangaFavorite(manga: Manga) {
|
||||
manga.favorite = !manga.favorite
|
||||
|
||||
when (manga.favorite) {
|
||||
true -> manga.date_added = Date().time
|
||||
false -> manga.date_added = 0
|
||||
}
|
||||
|
||||
db.insertManga(manga).executeAsBlocking()
|
||||
}
|
||||
|
||||
fun confirmDeletion(manga: Manga) {
|
||||
coverCache.deleteFromCache(manga)
|
||||
val downloadManager: DownloadManager = Injekt.get()
|
||||
@ -365,56 +347,4 @@ open class BrowseSourcePresenter(
|
||||
fun getCategories(): List<Category> {
|
||||
return db.getCategories().executeAsBlocking()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the category id's the manga is in, if the manga is not in a category, returns the default id.
|
||||
*
|
||||
* @param manga the manga to get categories from.
|
||||
* @return Array of category ids the manga is in, if none returns default id
|
||||
*/
|
||||
fun getMangaCategoryIds(manga: Manga): Array<Int?> {
|
||||
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
|
||||
return categories.mapNotNull { it.id }.toTypedArray()
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to categories.
|
||||
*
|
||||
* @param categories the selected categories.
|
||||
* @param manga the manga to move.
|
||||
*/
|
||||
private fun moveMangaToCategories(manga: Manga, categories: List<Category>) {
|
||||
val mc = categories.filter { it.id != 0 }.map { MangaCategory.create(manga, it) }
|
||||
db.setMangaCategories(mc, listOf(manga))
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given manga to the category.
|
||||
*
|
||||
* @param category the selected category.
|
||||
* @param manga the manga to move.
|
||||
*/
|
||||
fun moveMangaToCategory(manga: Manga, category: Category?) {
|
||||
moveMangaToCategories(manga, listOfNotNull(category))
|
||||
}
|
||||
|
||||
/**
|
||||
* Update manga to use selected categories.
|
||||
*
|
||||
* @param manga needed to change
|
||||
* @param selectedCategories selected categories
|
||||
*/
|
||||
fun updateMangaCategories(manga: Manga, selectedCategories: List<Category>) {
|
||||
if (selectedCategories.isNotEmpty()) {
|
||||
if (!manga.favorite) {
|
||||
changeMangaFavorite(manga)
|
||||
}
|
||||
|
||||
moveMangaToCategories(manga, selectedCategories.filter { it.id != 0 })
|
||||
} else {
|
||||
if (!manga.favorite) {
|
||||
changeMangaFavorite(manga)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,18 @@
|
||||
package eu.kanade.tachiyomi.util
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaCategory
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.ui.category.addtolibrary.SetCategoriesSheet
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import java.util.Date
|
||||
|
||||
fun Manga.isLocal() = source == LocalSource.ID
|
||||
|
||||
@ -25,3 +34,108 @@ fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper
|
||||
|
||||
return categoriesForManga.intersect(categoriesToDownload).isNotEmpty()
|
||||
}
|
||||
|
||||
fun Manga.moveCategories(
|
||||
db: DatabaseHelper,
|
||||
activity: Activity,
|
||||
onMangaMoved: () -> Unit
|
||||
) {
|
||||
val categories = db.getCategories().executeAsBlocking()
|
||||
val categoriesForManga = db.getCategoriesForManga(this).executeAsBlocking()
|
||||
val ids = categoriesForManga.mapNotNull { it.id }.toTypedArray()
|
||||
SetCategoriesSheet(
|
||||
activity,
|
||||
this,
|
||||
categories.toMutableList(),
|
||||
ids,
|
||||
false
|
||||
) {
|
||||
onMangaMoved()
|
||||
}.show()
|
||||
}
|
||||
|
||||
fun Manga.addOrRemoveToFavorites(
|
||||
db: DatabaseHelper,
|
||||
preferences: PreferencesHelper,
|
||||
view: View,
|
||||
activity: Activity,
|
||||
onMangaAdded: () -> Unit,
|
||||
onMangaMoved: () -> Unit,
|
||||
onMangaDeleted: () -> Unit
|
||||
): Snackbar? {
|
||||
if (!favorite) {
|
||||
val categories = db.getCategories().executeAsBlocking()
|
||||
val defaultCategoryId = preferences.defaultCategory()
|
||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
||||
when {
|
||||
defaultCategory != null -> {
|
||||
favorite = true
|
||||
date_added = Date().time
|
||||
db.insertManga(this).executeAsBlocking()
|
||||
val mc = MangaCategory.create(this, defaultCategory)
|
||||
db.setMangaCategories(listOf(mc), listOf(this))
|
||||
onMangaMoved()
|
||||
return view.snack(activity.getString(R.string.added_to_, defaultCategory.name)) {
|
||||
setAction(R.string.change) {
|
||||
moveCategories(db, activity, onMangaMoved)
|
||||
}
|
||||
}
|
||||
}
|
||||
defaultCategoryId == 0 || categories.isEmpty() -> { // 'Default' or no category
|
||||
favorite = true
|
||||
date_added = Date().time
|
||||
db.insertManga(this).executeAsBlocking()
|
||||
db.setMangaCategories(emptyList(), listOf(this))
|
||||
onMangaMoved()
|
||||
return if (categories.isNotEmpty()) {
|
||||
view.snack(activity.getString(R.string.added_to_, activity.getString(R.string.default_value))) {
|
||||
setAction(R.string.change) {
|
||||
moveCategories(db, activity, onMangaMoved)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
view.snack(R.string.added_to_library)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val categoriesForManga = db.getCategoriesForManga(this).executeAsBlocking()
|
||||
val ids = categoriesForManga.mapNotNull { it.id }.toTypedArray()
|
||||
|
||||
SetCategoriesSheet(
|
||||
activity,
|
||||
this,
|
||||
categories.toMutableList(),
|
||||
ids,
|
||||
true
|
||||
) {
|
||||
onMangaAdded()
|
||||
}.show()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val lastAddedDate = date_added
|
||||
favorite = false
|
||||
date_added = 0
|
||||
db.insertManga(this).executeAsBlocking()
|
||||
onMangaMoved()
|
||||
return view.snack(view.context.getString(R.string.removed_from_library), Snackbar.LENGTH_INDEFINITE) {
|
||||
setAction(R.string.undo) {
|
||||
favorite = true
|
||||
date_added = lastAddedDate
|
||||
db.insertManga(this@addOrRemoveToFavorites).executeAsBlocking()
|
||||
onMangaMoved()
|
||||
}
|
||||
addCallback(
|
||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!favorite) {
|
||||
onMangaDeleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
8
app/src/main/res/drawable/ic_plus_24dp.xml
Normal file
8
app/src/main/res/drawable/ic_plus_24dp.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<!-- drawable/plus.xml -->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:height="24dp"
|
||||
android:width="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path android:fillColor="#000" android:pathData="M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z" />
|
||||
</vector>
|
12
app/src/main/res/layout/add_category_item.xml
Normal file
12
app/src/main/res/layout/add_category_item.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.google.android.material.checkbox.MaterialCheckBox xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/category_checkbox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48sp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:textSize="15sp"
|
||||
tools:text="@string/category" />
|
@ -50,7 +50,7 @@
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="none"
|
||||
android:inputType="textCapSentences"
|
||||
android:maxLines="1"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/textColorPrimary"
|
||||
|
@ -2,17 +2,30 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/title"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/category_text_layout"
|
||||
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/title"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"/>
|
||||
android:layout_marginEnd="16dp"
|
||||
android:hint="@string/title"
|
||||
app:boxStrokeColor="@color/colorAccent"
|
||||
app:endIconMode="clear_text"
|
||||
app:hintEnabled="false"
|
||||
app:hintTextColor="@color/colorAccent">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/category"
|
||||
android:inputType="textCapSentences" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/download_new"
|
||||
|
131
app/src/main/res/layout/set_categories_sheet.xml
Normal file
131
app/src/main/res/layout/set_categories_sheet.xml
Normal file
@ -0,0 +1,131 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/source_filter_sheet"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bottom_sheet_rounded_background"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
android:backgroundTint="?android:attr/colorBackground">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/category_recycler_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@id/title_layout"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/button_layout"
|
||||
tools:listitem="@layout/add_category_item"/>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/title_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:layout_gravity="top"
|
||||
android:background="@drawable/bottom_sheet_rounded_background"
|
||||
android:backgroundTint="?attr/colorSecondary"
|
||||
android:elevation="0dp"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toTopOf="@id/category_recycler_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/toolbar_title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="20dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text="@string/add_x_to"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:textColor="?actionBarTintColor"
|
||||
android:textSize="17sp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/new_category_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Add manga to…"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/new_category_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
app:icon="@drawable/ic_plus_24dp"
|
||||
app:iconTint="@color/colorAccent"
|
||||
style="@style/Theme.Widget.Button.TextButton"
|
||||
android:text="@string/new_category"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/toolbar_title"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:background="@color/divider"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/button_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?actionBarSize"
|
||||
android:layout_gravity="top"
|
||||
android:background="@drawable/bottom_sheet_rounded_background"
|
||||
android:backgroundTint="?attr/colorSecondary"
|
||||
android:clickable="true"
|
||||
android:elevation="0dp"
|
||||
android:focusable="true"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/category_recycler_view">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/cancel_button"
|
||||
style="@style/Theme.Widget.Button.TextButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/cancel"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/add_to_categories_button"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/add_to_categories_button"
|
||||
style="@style/Theme.Widget.Button.Primary"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="48dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:text="@string/new_category"
|
||||
app:iconTint="@color/colorAccent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/cancel_button"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Add to Default" />
|
||||
|
||||
<View
|
||||
android:id="@+id/bottom_divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:background="@color/divider"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -21,6 +21,8 @@
|
||||
<string name="author">Author</string>
|
||||
<string name="artist">Artist</string>
|
||||
<string name="description">Description</string>
|
||||
<string name="move_x_to">Move %1$s to…</string>
|
||||
<string name="add_x_to">Add %1$s to…</string>
|
||||
|
||||
<!-- Status -->
|
||||
<string name="ongoing">Ongoing</string>
|
||||
@ -69,6 +71,7 @@
|
||||
<string name="adding_category_to_queue">Adding %1$s to update queue</string>
|
||||
<string name="_already_in_queue">%1$s is already in queue</string>
|
||||
<string name="create_new_category">Create new category</string>
|
||||
<string name="new_category">New category</string>
|
||||
<string name="category_is_empty">Category is empty</string>
|
||||
<string name="category_is_hidden">Category is hidden</string>
|
||||
<string name="top_category">Top category (%1$s)</string>
|
||||
@ -86,12 +89,12 @@
|
||||
<string name="manage_category">Manage category</string>
|
||||
<string name="rename_category">Rename category</string>
|
||||
<string name="move_to_categories">Move to categories</string>
|
||||
<string name="add_to_categories">Choose which categories to add this to. If none are selected, this will be added to the "default" category</string>
|
||||
<plurals name="category_plural">
|
||||
<item quantity="one">%d category</item>
|
||||
<item quantity="other">%d categories</item>
|
||||
</plurals>
|
||||
<string name="category_with_name_exists">A category with that name already exists!</string>
|
||||
<string name="category_cannot_be_blank">Category name cannot be blank</string>
|
||||
<string name="category_deleted">Category deleted</string>
|
||||
<string name="long_press_category">Press and hold to edit a category</string>
|
||||
<string name="jump_to_category">Jump to category</string>
|
||||
@ -788,6 +791,8 @@
|
||||
|
||||
<!-- Miscellaneous -->
|
||||
<string name="add">Add</string>
|
||||
<string name="add_to_">Add to %1$s</string>
|
||||
<string name="added_to_">Added to %1$s</string>
|
||||
<string name="all">All</string>
|
||||
<string name="alphabetically">Alphabetically</string>
|
||||
<string name="always">Always</string>
|
||||
@ -801,6 +806,7 @@
|
||||
<string name="bug_report">Report a Bug</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="center">Center</string>
|
||||
<string name="change">Change</string>
|
||||
<string name="charging">Charging</string>
|
||||
<string name="clear">Clear</string>
|
||||
<string name="close">Close</string>
|
||||
@ -839,6 +845,7 @@
|
||||
<string name="more">More</string>
|
||||
<string name="move_to_bottom">Move to bottom</string>
|
||||
<string name="move_to_top">Move to top</string>
|
||||
<string name="move_to_">Move to %1$s</string>
|
||||
<string name="moved_to_">Moved to %1$s</string>
|
||||
<string name="never">Never</string>
|
||||
<string name="newest">Newest</string>
|
||||
|
Loading…
Reference in New Issue
Block a user