mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-14 04:15:09 +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 {
|
companion object {
|
||||||
private const val DRAG_AND_DROP = 'D'
|
const val DRAG_AND_DROP = 'D'
|
||||||
private const val ALPHA_ASC = 'a'
|
const val ALPHA_ASC = 'a'
|
||||||
private const val ALPHA_DSC = 'b'
|
const val ALPHA_DSC = 'b'
|
||||||
private const val UPDATED_ASC = 'c'
|
const val UPDATED_ASC = 'c'
|
||||||
private const val UPDATED_DSC = 'd'
|
const val UPDATED_DSC = 'd'
|
||||||
private const val UNREAD_ASC = 'e'
|
const val UNREAD_ASC = 'e'
|
||||||
private const val UNREAD_DSC = 'f'
|
const val UNREAD_DSC = 'f'
|
||||||
private const val LAST_READ_ASC = 'g'
|
const val LAST_READ_ASC = 'g'
|
||||||
private const val LAST_READ_DSC = 'h'
|
const val LAST_READ_DSC = 'h'
|
||||||
private const val TOTAL_ASC = 'i'
|
const val TOTAL_ASC = 'i'
|
||||||
private const val TOTAL_DSC = 'j'
|
const val TOTAL_DSC = 'j'
|
||||||
private const val DATE_ADDED_ASC = 'k'
|
const val DATE_ADDED_ASC = 'k'
|
||||||
private const val DATE_ADDED_DSC = 'l'
|
const val DATE_ADDED_DSC = 'l'
|
||||||
|
|
||||||
fun create(name: String): Category = CategoryImpl().apply {
|
fun create(name: String): Category = CategoryImpl().apply {
|
||||||
this.name = name
|
this.name = name
|
||||||
|
@ -72,10 +72,10 @@ class CategoryPresenter(
|
|||||||
val cat = Category.create(name)
|
val cat = Category.create(name)
|
||||||
|
|
||||||
// Set the new item in the last position.
|
// 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.
|
// Insert into database.
|
||||||
cat.mangaSort = 'a'
|
cat.mangaSort = Category.ALPHA_ASC
|
||||||
db.insertCategory(cat).executeAsBlocking()
|
db.insertCategory(cat).executeAsBlocking()
|
||||||
val cats = db.getCategories().executeAsBlocking()
|
val cats = db.getCategories().executeAsBlocking()
|
||||||
val newCat = cats.find { it.name == name } ?: return false
|
val newCat = cats.find { it.name == name } ?: return false
|
||||||
|
@ -1,9 +1,13 @@
|
|||||||
package eu.kanade.tachiyomi.ui.category
|
package eu.kanade.tachiyomi.ui.category
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.app.Dialog
|
import android.app.Dialog
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.widget.CompoundButton
|
import android.widget.CompoundButton
|
||||||
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.core.widget.addTextChangedListener
|
||||||
import com.afollestad.materialdialogs.MaterialDialog
|
import com.afollestad.materialdialogs.MaterialDialog
|
||||||
|
import com.afollestad.materialdialogs.callbacks.onShow
|
||||||
import com.afollestad.materialdialogs.customview.customView
|
import com.afollestad.materialdialogs.customview.customView
|
||||||
import com.afollestad.materialdialogs.customview.getCustomView
|
import com.afollestad.materialdialogs.customview.getCustomView
|
||||||
import com.tfcporciuncula.flow.Preference
|
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.data.preference.getOrDefault
|
||||||
import eu.kanade.tachiyomi.databinding.MangaCategoryDialogBinding
|
import eu.kanade.tachiyomi.databinding.MangaCategoryDialogBinding
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
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.gone
|
||||||
import eu.kanade.tachiyomi.util.view.visible
|
import eu.kanade.tachiyomi.util.view.visible
|
||||||
import eu.kanade.tachiyomi.util.view.visibleIf
|
import eu.kanade.tachiyomi.util.view.visibleIf
|
||||||
@ -26,12 +28,12 @@ import uy.kohesive.injekt.injectLazy
|
|||||||
class ManageCategoryDialog(bundle: Bundle? = null) :
|
class ManageCategoryDialog(bundle: Bundle? = null) :
|
||||||
DialogController(bundle) {
|
DialogController(bundle) {
|
||||||
|
|
||||||
constructor(libraryController: LibraryController, category: Category) : this() {
|
constructor(category: Category?, updateLibrary: ((Int?) -> Unit)) : this() {
|
||||||
this.libraryController = libraryController
|
this.updateLibrary = updateLibrary
|
||||||
this.category = category
|
this.category = category
|
||||||
}
|
}
|
||||||
|
|
||||||
private var libraryController: LibraryController? = null
|
private var updateLibrary: ((Int?) -> Unit)? = null
|
||||||
private var category: Category? = null
|
private var category: Category? = null
|
||||||
|
|
||||||
private val preferences by injectLazy<PreferencesHelper>()
|
private val preferences by injectLazy<PreferencesHelper>()
|
||||||
@ -39,28 +41,59 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
|||||||
lateinit var binding: MangaCategoryDialogBinding
|
lateinit var binding: MangaCategoryDialogBinding
|
||||||
|
|
||||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||||
val dialog = MaterialDialog(activity!!).apply {
|
val dialog = dialog(activity!!)
|
||||||
title(R.string.manage_category)
|
|
||||||
customView(viewRes = R.layout.manga_category_dialog)
|
|
||||||
negativeButton(android.R.string.cancel)
|
|
||||||
positiveButton(R.string.save) { onPositiveButtonClick() }
|
|
||||||
}
|
|
||||||
binding = MangaCategoryDialogBinding.bind(dialog.getCustomView())
|
binding = MangaCategoryDialogBinding.bind(dialog.getCustomView())
|
||||||
onViewCreated()
|
onViewCreated()
|
||||||
return dialog
|
return dialog
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onPositiveButtonClick() {
|
fun dialog(activity: Activity): MaterialDialog {
|
||||||
val category = category ?: return
|
return MaterialDialog(activity).apply {
|
||||||
if (category.id ?: 0 <= 0) return
|
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 text = binding.title.text.toString()
|
||||||
val categoryExists = categoryExists(text)
|
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
|
category.name = text
|
||||||
|
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()
|
db.insertCategory(category).executeAsBlocking()
|
||||||
libraryController?.presenter?.getLibrary()
|
}
|
||||||
} else if (categoryExists) {
|
} 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)) {
|
if (!updatePref(preferences.downloadNewCategories(), binding.downloadNew)) {
|
||||||
preferences.downloadNew().set(false)
|
preferences.downloadNew().set(false)
|
||||||
@ -73,6 +106,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
|||||||
preferences.libraryUpdateInterval().set(0)
|
preferences.libraryUpdateInterval().set(0)
|
||||||
LibraryUpdateJob.setupTask(0)
|
LibraryUpdateJob.setupTask(0)
|
||||||
}
|
}
|
||||||
|
updateLibrary?.invoke(category.id)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,19 +120,22 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onViewCreated() {
|
fun onViewCreated() {
|
||||||
val category = category ?: return
|
if (category?.id ?: 0 <= 0 && category != null) {
|
||||||
if (category.id ?: 0 <= 0) {
|
|
||||||
binding.title.gone()
|
binding.title.gone()
|
||||||
binding.downloadNew.gone()
|
binding.downloadNew.gone()
|
||||||
binding.includeGlobal.gone()
|
binding.includeGlobal.gone()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
binding.editCategories.isVisible = category != null
|
||||||
binding.editCategories.setOnClickListener {
|
binding.editCategories.setOnClickListener {
|
||||||
router.popCurrentController()
|
router.popCurrentController()
|
||||||
router.pushController(CategoryController().withFadeTransaction())
|
router.pushController(CategoryController().withFadeTransaction())
|
||||||
}
|
}
|
||||||
binding.title.hint = category.name
|
binding.title.addTextChangedListener {
|
||||||
binding.title.append(category.name)
|
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()
|
val downloadNew = preferences.downloadNew().get()
|
||||||
setCheckbox(
|
setCheckbox(
|
||||||
binding.downloadNew,
|
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) {
|
override fun manageCategory(position: Int) {
|
||||||
val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return
|
val category = (adapter.getItem(position) as? LibraryHeaderItem)?.category ?: return
|
||||||
if (!category.isDynamic) {
|
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.BaseController
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
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.library.LibraryController
|
||||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.main.SearchActivity
|
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.BrowseController
|
||||||
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
import eu.kanade.tachiyomi.ui.source.global_search.GlobalSearchController
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
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.storage.getUriCompat
|
||||||
import eu.kanade.tachiyomi.util.system.ThemeUtil
|
import eu.kanade.tachiyomi.util.system.ThemeUtil
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
@ -116,9 +116,7 @@ class MangaDetailsController :
|
|||||||
FlexibleAdapter.OnItemLongClickListener,
|
FlexibleAdapter.OnItemLongClickListener,
|
||||||
ActionMode.Callback,
|
ActionMode.Callback,
|
||||||
MangaDetailsAdapter.MangaDetailsInterface,
|
MangaDetailsAdapter.MangaDetailsInterface,
|
||||||
FlexibleAdapter.OnItemMoveListener,
|
FlexibleAdapter.OnItemMoveListener {
|
||||||
ChangeMangaCategoriesDialog.Listener,
|
|
||||||
AddToLibraryCategoriesDialog.Listener {
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
manga: Manga?,
|
manga: Manga?,
|
||||||
@ -1092,7 +1090,7 @@ class MangaDetailsController :
|
|||||||
popupView.setOnTouchListener(popup.dragToOpenListener)
|
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)
|
val popup = PopupMenu(view!!.context, popupView)
|
||||||
popup.menu.add(0, 1, 0, R.string.remove_from_library)
|
popup.menu.add(0, 1, 0, R.string.remove_from_library)
|
||||||
if (categories.isNotEmpty()) {
|
if (categories.isNotEmpty()) {
|
||||||
@ -1102,18 +1100,9 @@ class MangaDetailsController :
|
|||||||
// Set a listener so we are notified if a menu item is clicked
|
// Set a listener so we are notified if a menu item is clicked
|
||||||
popup.setOnMenuItemClickListener { menuItem ->
|
popup.setOnMenuItemClickListener { menuItem ->
|
||||||
if (menuItem.itemId == 0) {
|
if (menuItem.itemId == 0) {
|
||||||
val ids = presenter.getMangaCategoryIds()
|
presenter.manga.moveCategories(presenter.db, activity!!) {
|
||||||
val preselected = ids.mapNotNull { id ->
|
updateHeader()
|
||||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
}
|
||||||
}.toTypedArray()
|
|
||||||
ChangeMangaCategoriesDialog(
|
|
||||||
this,
|
|
||||||
listOf(manga),
|
|
||||||
categories,
|
|
||||||
preselected
|
|
||||||
).showDialog(
|
|
||||||
router
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
toggleMangaFavorite()
|
toggleMangaFavorite()
|
||||||
}
|
}
|
||||||
@ -1123,31 +1112,24 @@ class MangaDetailsController :
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun toggleMangaFavorite() {
|
private fun toggleMangaFavorite() {
|
||||||
if (presenter.toggleFavorite()) {
|
val view = view ?: return
|
||||||
val categories = presenter.getCategories()
|
val activity = activity ?: return
|
||||||
val defaultCategoryId = presenter.preferences.defaultCategory()
|
snack?.dismiss()
|
||||||
val defaultCategory = categories.find { it.id == defaultCategoryId }
|
snack = presenter.manga.addOrRemoveToFavorites(
|
||||||
when {
|
presenter.db,
|
||||||
defaultCategory != null -> presenter.moveMangaToCategory(defaultCategory)
|
presenter.preferences,
|
||||||
defaultCategoryId == 0 || categories.isEmpty() -> // 'Default' or no category
|
view,
|
||||||
presenter.moveMangaToCategory(null)
|
activity,
|
||||||
else -> {
|
onMangaAdded = {
|
||||||
val ids = presenter.getMangaCategoryIds()
|
updateHeader()
|
||||||
val preselected = ids.mapNotNull { id ->
|
|
||||||
categories.indexOfFirst { it.id == id }.takeIf { it != -1 }
|
|
||||||
}.toTypedArray()
|
|
||||||
|
|
||||||
AddToLibraryCategoriesDialog(
|
|
||||||
this,
|
|
||||||
presenter.manga,
|
|
||||||
categories,
|
|
||||||
preselected
|
|
||||||
).showDialog(router)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
showAddedSnack()
|
showAddedSnack()
|
||||||
} else {
|
},
|
||||||
showRemovedSnack()
|
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))
|
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 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
|
* 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.Category
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter
|
import eu.kanade.tachiyomi.data.database.models.Chapter
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.Track
|
||||||
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
import eu.kanade.tachiyomi.data.database.models.toMangaInfo
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
@ -61,7 +60,7 @@ class MangaDetailsPresenter(
|
|||||||
val source: Source,
|
val source: Source,
|
||||||
val preferences: PreferencesHelper = Injekt.get(),
|
val preferences: PreferencesHelper = Injekt.get(),
|
||||||
val coverCache: CoverCache = 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 downloadManager: DownloadManager = Injekt.get(),
|
||||||
private val chapterFilter: ChapterFilter = Injekt.get()
|
private val chapterFilter: ChapterFilter = Injekt.get()
|
||||||
) : DownloadQueue.DownloadListener, LibraryServiceListener {
|
) : DownloadQueue.DownloadListener, LibraryServiceListener {
|
||||||
@ -334,7 +333,6 @@ class MangaDetailsPresenter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val networkManga = nManga.await()
|
val networkManga = nManga.await()
|
||||||
val mangaWasInitalized = manga.initialized
|
|
||||||
if (networkManga != null) {
|
if (networkManga != null) {
|
||||||
manga.copyFrom(networkManga)
|
manga.copyFrom(networkManga)
|
||||||
manga.initialized = true
|
manga.initialized = true
|
||||||
@ -405,7 +403,7 @@ class MangaDetailsPresenter(
|
|||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
|
withContext(Dispatchers.Main) { controller.showError(trimException(e)) }
|
||||||
return@launch
|
return@launch
|
||||||
} ?: listOf()
|
}
|
||||||
isLoading = false
|
isLoading = false
|
||||||
try {
|
try {
|
||||||
syncChaptersWithSource(db, chapters, manga, source)
|
syncChaptersWithSource(db, chapters, manga, source)
|
||||||
@ -551,38 +549,6 @@ class MangaDetailsPresenter(
|
|||||||
return db.getCategories().executeAsBlocking()
|
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() {
|
fun confirmDeletion() {
|
||||||
coverCache.deleteFromCache(manga)
|
coverCache.deleteFromCache(manga)
|
||||||
db.resetMangaInfo(manga).executeAsBlocking()
|
db.resetMangaInfo(manga).executeAsBlocking()
|
||||||
|
@ -11,13 +11,11 @@ import androidx.appcompat.widget.SearchView
|
|||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
|
import com.jakewharton.rxbinding.support.v7.widget.queryTextChangeEvents
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
import eu.davidea.flexibleadapter.items.IFlexible
|
import eu.davidea.flexibleadapter.items.IFlexible
|
||||||
import eu.kanade.tachiyomi.R
|
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.database.models.Manga
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
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.model.FilterList
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
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.main.MainActivity
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
|
||||||
import eu.kanade.tachiyomi.ui.source.BrowseController
|
import eu.kanade.tachiyomi.ui.source.BrowseController
|
||||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
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.connectivityManager
|
||||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||||
@ -60,8 +58,7 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
NucleusController<BrowseSourceControllerBinding, BrowseSourcePresenter>(bundle),
|
NucleusController<BrowseSourceControllerBinding, BrowseSourcePresenter>(bundle),
|
||||||
FlexibleAdapter.OnItemClickListener,
|
FlexibleAdapter.OnItemClickListener,
|
||||||
FlexibleAdapter.OnItemLongClickListener,
|
FlexibleAdapter.OnItemLongClickListener,
|
||||||
FlexibleAdapter.EndlessScrollListener,
|
FlexibleAdapter.EndlessScrollListener {
|
||||||
AddToLibraryCategoriesDialog.Listener {
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
source: CatalogueSource,
|
source: CatalogueSource,
|
||||||
@ -575,73 +572,23 @@ open class BrowseSourceController(bundle: Bundle) :
|
|||||||
*/
|
*/
|
||||||
override fun onItemLongClick(position: Int) {
|
override fun onItemLongClick(position: Int) {
|
||||||
val manga = (adapter?.getItem(position) as? BrowseSourceItem?)?.manga ?: return
|
val manga = (adapter?.getItem(position) as? BrowseSourceItem?)?.manga ?: return
|
||||||
|
val view = view ?: return
|
||||||
|
val activity = activity ?: return
|
||||||
snack?.dismiss()
|
snack?.dismiss()
|
||||||
if (manga.favorite) {
|
snack = manga.addOrRemoveToFavorites(
|
||||||
presenter.changeMangaFavorite(manga)
|
presenter.db,
|
||||||
|
preferences,
|
||||||
|
view,
|
||||||
|
activity,
|
||||||
|
onMangaAdded = {
|
||||||
adapter?.notifyItemChanged(position)
|
adapter?.notifyItemChanged(position)
|
||||||
snack = binding.sourceLayout.snack(R.string.removed_from_library, Snackbar.LENGTH_INDEFINITE) {
|
snack = view.snack(R.string.added_to_library)
|
||||||
setAction(R.string.undo) {
|
},
|
||||||
if (!manga.favorite) addManga(manga, position)
|
onMangaMoved = { adapter?.notifyItemChanged(position) },
|
||||||
}
|
onMangaDeleted = { presenter.confirmDeletion(manga) }
|
||||||
addCallback(
|
|
||||||
object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
|
||||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
|
||||||
super.onDismissed(transientBottomBar, event)
|
|
||||||
if (!manga.favorite) presenter.confirmDeletion(manga)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
|
||||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
(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.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
import eu.kanade.tachiyomi.data.database.models.Category
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
@ -37,7 +36,6 @@ import rx.subjects.PublishSubject
|
|||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.Date
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Presenter of [BrowseSourceController].
|
* Presenter of [BrowseSourceController].
|
||||||
@ -45,7 +43,7 @@ import java.util.Date
|
|||||||
open class BrowseSourcePresenter(
|
open class BrowseSourcePresenter(
|
||||||
sourceId: Long,
|
sourceId: Long,
|
||||||
sourceManager: SourceManager = Injekt.get(),
|
sourceManager: SourceManager = Injekt.get(),
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
val db: DatabaseHelper = Injekt.get(),
|
||||||
private val prefs: PreferencesHelper = Injekt.get(),
|
private val prefs: PreferencesHelper = Injekt.get(),
|
||||||
private val coverCache: CoverCache = Injekt.get()
|
private val coverCache: CoverCache = Injekt.get()
|
||||||
) : BasePresenter<BrowseSourceController>() {
|
) : BasePresenter<BrowseSourceController>() {
|
||||||
@ -278,22 +276,6 @@ open class BrowseSourcePresenter(
|
|||||||
.onErrorResumeNext { Observable.just(manga) }
|
.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) {
|
fun confirmDeletion(manga: Manga) {
|
||||||
coverCache.deleteFromCache(manga)
|
coverCache.deleteFromCache(manga)
|
||||||
val downloadManager: DownloadManager = Injekt.get()
|
val downloadManager: DownloadManager = Injekt.get()
|
||||||
@ -365,56 +347,4 @@ open class BrowseSourcePresenter(
|
|||||||
fun getCategories(): List<Category> {
|
fun getCategories(): List<Category> {
|
||||||
return db.getCategories().executeAsBlocking()
|
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
|
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.DatabaseHelper
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
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.data.preference.PreferencesHelper
|
||||||
import eu.kanade.tachiyomi.source.LocalSource
|
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
|
fun Manga.isLocal() = source == LocalSource.ID
|
||||||
|
|
||||||
@ -25,3 +34,108 @@ fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper
|
|||||||
|
|
||||||
return categoriesForManga.intersect(categoriesToDownload).isNotEmpty()
|
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:layout_height="match_parent"
|
||||||
android:background="@null"
|
android:background="@null"
|
||||||
android:imeOptions="actionDone"
|
android:imeOptions="actionDone"
|
||||||
android:inputType="none"
|
android:inputType="textCapSentences"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:textColor="@color/textColorPrimary"
|
android:textColor="@color/textColorPrimary"
|
||||||
|
@ -2,17 +2,30 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<EditText
|
<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:layout_marginStart="16dp"
|
||||||
|
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:id="@+id/title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/title"
|
android:hint="@string/category"
|
||||||
android:layout_marginEnd="16dp"
|
android:inputType="textCapSentences" />
|
||||||
android:layout_marginStart="16dp"
|
|
||||||
android:inputType="text"
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
android:maxLines="1"/>
|
|
||||||
|
|
||||||
<com.google.android.material.checkbox.MaterialCheckBox
|
<com.google.android.material.checkbox.MaterialCheckBox
|
||||||
android:id="@+id/download_new"
|
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="author">Author</string>
|
||||||
<string name="artist">Artist</string>
|
<string name="artist">Artist</string>
|
||||||
<string name="description">Description</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 -->
|
<!-- Status -->
|
||||||
<string name="ongoing">Ongoing</string>
|
<string name="ongoing">Ongoing</string>
|
||||||
@ -69,6 +71,7 @@
|
|||||||
<string name="adding_category_to_queue">Adding %1$s to update queue</string>
|
<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="_already_in_queue">%1$s is already in queue</string>
|
||||||
<string name="create_new_category">Create new category</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_empty">Category is empty</string>
|
||||||
<string name="category_is_hidden">Category is hidden</string>
|
<string name="category_is_hidden">Category is hidden</string>
|
||||||
<string name="top_category">Top category (%1$s)</string>
|
<string name="top_category">Top category (%1$s)</string>
|
||||||
@ -86,12 +89,12 @@
|
|||||||
<string name="manage_category">Manage category</string>
|
<string name="manage_category">Manage category</string>
|
||||||
<string name="rename_category">Rename category</string>
|
<string name="rename_category">Rename category</string>
|
||||||
<string name="move_to_categories">Move to categories</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">
|
<plurals name="category_plural">
|
||||||
<item quantity="one">%d category</item>
|
<item quantity="one">%d category</item>
|
||||||
<item quantity="other">%d categories</item>
|
<item quantity="other">%d categories</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="category_with_name_exists">A category with that name already exists!</string>
|
<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="category_deleted">Category deleted</string>
|
||||||
<string name="long_press_category">Press and hold to edit a category</string>
|
<string name="long_press_category">Press and hold to edit a category</string>
|
||||||
<string name="jump_to_category">Jump to category</string>
|
<string name="jump_to_category">Jump to category</string>
|
||||||
@ -788,6 +791,8 @@
|
|||||||
|
|
||||||
<!-- Miscellaneous -->
|
<!-- Miscellaneous -->
|
||||||
<string name="add">Add</string>
|
<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="all">All</string>
|
||||||
<string name="alphabetically">Alphabetically</string>
|
<string name="alphabetically">Alphabetically</string>
|
||||||
<string name="always">Always</string>
|
<string name="always">Always</string>
|
||||||
@ -801,6 +806,7 @@
|
|||||||
<string name="bug_report">Report a Bug</string>
|
<string name="bug_report">Report a Bug</string>
|
||||||
<string name="cancel">Cancel</string>
|
<string name="cancel">Cancel</string>
|
||||||
<string name="center">Center</string>
|
<string name="center">Center</string>
|
||||||
|
<string name="change">Change</string>
|
||||||
<string name="charging">Charging</string>
|
<string name="charging">Charging</string>
|
||||||
<string name="clear">Clear</string>
|
<string name="clear">Clear</string>
|
||||||
<string name="close">Close</string>
|
<string name="close">Close</string>
|
||||||
@ -839,6 +845,7 @@
|
|||||||
<string name="more">More</string>
|
<string name="more">More</string>
|
||||||
<string name="move_to_bottom">Move to bottom</string>
|
<string name="move_to_bottom">Move to bottom</string>
|
||||||
<string name="move_to_top">Move to top</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="moved_to_">Moved to %1$s</string>
|
||||||
<string name="never">Never</string>
|
<string name="never">Never</string>
|
||||||
<string name="newest">Newest</string>
|
<string name="newest">Newest</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user