mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-11-09 19:55:11 +01:00
Category controller is no longer rx, md2 design (really just google keep)
This commit is contained in:
parent
0bc81a8237
commit
fc0ab3e878
@ -41,7 +41,7 @@ class SourceHolder(view: View, override val adapter: CatalogueAdapter) :
|
||||
|
||||
// Set circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(), false))
|
||||
edit_button.setImageDrawable(edit_button.getRound(source.name.take(1).toUpperCase(), false))
|
||||
}
|
||||
|
||||
// If source is login, show only login option
|
||||
|
@ -13,38 +13,26 @@ class CategoryAdapter(controller: CategoryController) :
|
||||
/**
|
||||
* Listener called when an item of the list is released.
|
||||
*/
|
||||
val onItemReleaseListener: OnItemReleaseListener = controller
|
||||
|
||||
/**
|
||||
* Clears the active selections from the list and the model.
|
||||
*/
|
||||
override fun clearSelection() {
|
||||
super.clearSelection()
|
||||
(0 until itemCount).forEach { getItem(it)?.isSelected = false }
|
||||
}
|
||||
val categoryItemListener: CategoryItemListener = controller
|
||||
|
||||
/**
|
||||
* Clears the active selections from the model.
|
||||
*/
|
||||
fun clearModelSelection() {
|
||||
selectedPositions.forEach { getItem(it)?.isSelected = false }
|
||||
fun resetEditing(position: Int) {
|
||||
for (i in 0..itemCount) {
|
||||
getItem(i)?.isEditing = false
|
||||
}
|
||||
getItem(position)?.isEditing = true
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the selection of the given position.
|
||||
*
|
||||
* @param position The position to toggle.
|
||||
*/
|
||||
override fun toggleSelection(position: Int) {
|
||||
super.toggleSelection(position)
|
||||
getItem(position)?.isSelected = isSelected(position)
|
||||
}
|
||||
|
||||
interface OnItemReleaseListener {
|
||||
interface CategoryItemListener {
|
||||
/**
|
||||
* Called when an item of the list is released.
|
||||
*/
|
||||
fun onItemReleased(position: Int)
|
||||
fun onCategoryRename(position: Int, newName: String): Boolean
|
||||
fun onItemDelete(position: Int)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,46 +1,33 @@
|
||||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.*
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import com.google.android.material.snackbar.BaseTransientBottomBar
|
||||
import com.jakewharton.rxbinding.view.clicks
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
import eu.davidea.flexibleadapter.helpers.UndoHelper
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.BaseController
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Companion.CREATE_CATEGORY_ORDER
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
||||
import eu.kanade.tachiyomi.util.view.marginBottom
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.android.synthetic.main.categories_controller.empty_view
|
||||
import kotlinx.android.synthetic.main.categories_controller.fab
|
||||
import kotlinx.android.synthetic.main.categories_controller.recycler
|
||||
import eu.kanade.tachiyomi.util.view.snack
|
||||
import kotlinx.android.synthetic.main.categories_controller.*
|
||||
|
||||
/**
|
||||
* Controller to manage the categories for the users' library.
|
||||
*/
|
||||
class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
ActionMode.Callback,
|
||||
class CategoryController(bundle: Bundle? = null) : BaseController(bundle),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
CategoryAdapter.OnItemReleaseListener,
|
||||
CategoryAdapter.CategoryItemListener,
|
||||
CategoryCreateDialog.Listener,
|
||||
CategoryRenameDialog.Listener,
|
||||
UndoHelper.OnActionListener {
|
||||
|
||||
/**
|
||||
* Object used to show ActionMode toolbar.
|
||||
*/
|
||||
private var actionMode: ActionMode? = null
|
||||
CategoryRenameDialog.Listener {
|
||||
|
||||
/**
|
||||
* Adapter containing category items.
|
||||
@ -55,7 +42,7 @@ class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
/**
|
||||
* Creates the presenter for this controller. Not to be manually called.
|
||||
*/
|
||||
override fun createPresenter() = CategoryPresenter()
|
||||
private val presenter = CategoryPresenter(this)
|
||||
|
||||
/**
|
||||
* Returns the toolbar title to show when this controller is attached.
|
||||
@ -89,20 +76,8 @@ class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
adapter?.isHandleDragEnabled = true
|
||||
adapter?.isPermanentDelete = false
|
||||
|
||||
fab.clicks().subscribeUntilDestroy {
|
||||
CategoryCreateDialog(this@CategoryController).showDialog(router, null)
|
||||
}
|
||||
|
||||
val fabBaseMarginBottom = fab?.marginBottom ?: 0
|
||||
recycler.doOnApplyWindowInsets { v, insets, padding ->
|
||||
|
||||
fab?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
bottomMargin = fabBaseMarginBottom + insets.systemWindowInsetBottom
|
||||
}
|
||||
// offset the recycler by the fab's inset + some inset on top
|
||||
v.updatePaddingRelative(bottom = padding.bottom + (fab?.marginBottom ?: 0) +
|
||||
fabBaseMarginBottom + (fab?.height ?: 0))
|
||||
}
|
||||
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE)
|
||||
presenter.getCategories()
|
||||
}
|
||||
|
||||
/**
|
||||
@ -113,125 +88,35 @@ class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
override fun onDestroyView(view: View) {
|
||||
// Manually call callback to delete categories if required
|
||||
snack?.dismiss()
|
||||
view.clearFocus()
|
||||
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
|
||||
confirmDelete()
|
||||
snack = null
|
||||
actionMode = null
|
||||
adapter = null
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
override fun handleBack(): Boolean {
|
||||
view?.clearFocus()
|
||||
confirmDelete()
|
||||
activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
|
||||
return super.handleBack()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter when the categories are updated.
|
||||
*
|
||||
* @param categories The new list of categories to display.
|
||||
*/
|
||||
fun setCategories(categories: List<CategoryItem>) {
|
||||
actionMode?.finish()
|
||||
adapter?.updateDataSet(categories)
|
||||
if (categories.isNotEmpty()) {
|
||||
empty_view.hide()
|
||||
val selected = categories.filter { it.isSelected }
|
||||
if (selected.isNotEmpty()) {
|
||||
selected.forEach { onItemLongClick(categories.indexOf(it)) }
|
||||
}
|
||||
} else {
|
||||
empty_view.show(R.drawable.ic_shape_black_128dp, R.string.information_empty_category)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when action mode is first created. The menu supplied will be used to generate action
|
||||
* buttons for the action mode.
|
||||
*
|
||||
* @param mode ActionMode being created.
|
||||
* @param menu Menu used to populate action buttons.
|
||||
* @return true if the action mode should be created, false if entering this mode should be
|
||||
* aborted.
|
||||
*/
|
||||
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
// Inflate menu.
|
||||
mode.menuInflater.inflate(R.menu.category_selection, menu)
|
||||
// Enable adapter multi selection.
|
||||
adapter?.mode = SelectableAdapter.Mode.MULTI
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to refresh an action mode's action menu whenever it is invalidated.
|
||||
*
|
||||
* @param mode ActionMode being prepared.
|
||||
* @param menu Menu used to populate action buttons.
|
||||
* @return true if the menu or action mode was updated, false otherwise.
|
||||
*/
|
||||
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
val count = adapter.selectedItemCount
|
||||
mode.title = resources?.getString(R.string.label_selected, count)
|
||||
|
||||
// Show edit button only when one item is selected
|
||||
val editItem = mode.menu.findItem(R.id.action_edit)
|
||||
editItem.isVisible = count == 1
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report a user click on an action button.
|
||||
*
|
||||
* @param mode The current ActionMode.
|
||||
* @param item The item that was clicked.
|
||||
* @return true if this callback handled the event, false if the standard MenuItem invocation
|
||||
* should continue.
|
||||
*/
|
||||
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
|
||||
when (item.itemId) {
|
||||
R.id.action_delete -> {
|
||||
adapter.removeItems(adapter.selectedPositions)
|
||||
snack =
|
||||
view?.snack(R.string.snack_categories_deleted, Snackbar.LENGTH_INDEFINITE) {
|
||||
var undoing = false
|
||||
setAction(R.string.action_undo) {
|
||||
adapter.restoreDeletedItems()
|
||||
undoing = true
|
||||
}
|
||||
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!undoing) confirmDelete()
|
||||
}
|
||||
})
|
||||
}
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
||||
mode.finish()
|
||||
}
|
||||
R.id.action_edit -> {
|
||||
// Edit selected category
|
||||
if (adapter.selectedItemCount == 1) {
|
||||
val position = adapter.selectedPositions.first()
|
||||
val category = adapter.getItem(position)?.category
|
||||
if (category != null) {
|
||||
editCategory(category)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an action mode is about to be exited and destroyed.
|
||||
*
|
||||
* @param mode The current ActionMode being destroyed.
|
||||
*/
|
||||
override fun onDestroyActionMode(mode: ActionMode) {
|
||||
// Reset adapter to single selection
|
||||
adapter?.mode = SelectableAdapter.Mode.IDLE
|
||||
adapter?.clearSelection()
|
||||
actionMode = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an item in the list is clicked.
|
||||
*
|
||||
@ -239,50 +124,45 @@ class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
* @return true if this click should enable selection mode.
|
||||
*/
|
||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||
// Check if action mode is initialized and selected item exist.
|
||||
return if (actionMode != null && position != RecyclerView.NO_POSITION) {
|
||||
toggleSelection(position)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
adapter?.resetEditing(position)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an item in the list is long clicked.
|
||||
*
|
||||
* @param position The position of the clicked item.
|
||||
*/
|
||||
override fun onItemLongClick(position: Int) {
|
||||
val activity = activity as? AppCompatActivity ?: return
|
||||
|
||||
// Check if action mode is initialized.
|
||||
if (actionMode == null) {
|
||||
// Initialize action mode
|
||||
actionMode = activity.startSupportActionMode(this)
|
||||
}
|
||||
|
||||
// Set item as selected
|
||||
toggleSelection(position)
|
||||
override fun onCategoryRename(position: Int, newName: String): Boolean {
|
||||
val category = adapter?.getItem(position)?.category ?: return false
|
||||
if (category.order == CREATE_CATEGORY_ORDER)
|
||||
return (presenter.createCategory(newName))
|
||||
return (presenter.renameCategory(category, newName))
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle the selection state of an item.
|
||||
* If the item was the last one in the selection and is unselected, the ActionMode is finished.
|
||||
*
|
||||
* @param position The position of the item to toggle.
|
||||
*/
|
||||
private fun toggleSelection(position: Int) {
|
||||
val adapter = adapter ?: return
|
||||
override fun onItemDelete(position: Int) {
|
||||
MaterialDialog(activity!!)
|
||||
.title(R.string.confirm_category_deletion)
|
||||
.message(R.string.confirm_category_deletion_message)
|
||||
.positiveButton(R.string.action_delete) {
|
||||
deleteCategory(position)
|
||||
}
|
||||
.negativeButton(android.R.string.no)
|
||||
.show()
|
||||
}
|
||||
|
||||
//Mark the position selected
|
||||
adapter.toggleSelection(position)
|
||||
|
||||
if (adapter.selectedItemCount == 0) {
|
||||
actionMode?.finish()
|
||||
} else {
|
||||
actionMode?.invalidate()
|
||||
}
|
||||
private fun deleteCategory(position: Int) {
|
||||
adapter?.removeItem(position)
|
||||
snack =
|
||||
view?.snack(R.string.snack_category_deleted, Snackbar.LENGTH_INDEFINITE) {
|
||||
var undoing = false
|
||||
setAction(R.string.action_undo) {
|
||||
adapter?.restoreDeletedItems()
|
||||
undoing = true
|
||||
}
|
||||
addCallback(object : BaseTransientBottomBar.BaseCallback<Snackbar>() {
|
||||
override fun onDismissed(transientBottomBar: Snackbar?, event: Int) {
|
||||
super.onDismissed(transientBottomBar, event)
|
||||
if (!undoing) confirmDelete()
|
||||
}
|
||||
})
|
||||
}
|
||||
(activity as? MainActivity)?.setUndoSnackBar(snack)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -296,31 +176,10 @@ class CategoryController : NucleusController<CategoryPresenter>(),
|
||||
presenter.reorderCategories(categories)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the undo action is clicked in the snackbar.
|
||||
*
|
||||
* @param action The action performed.
|
||||
*/
|
||||
override fun onActionCanceled(action: Int, positions: MutableList<Int>?) {
|
||||
adapter?.restoreDeletedItems()
|
||||
snack = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the time to restore the items expires.
|
||||
*
|
||||
* @param action The action performed.
|
||||
* @param event The event that triggered the action
|
||||
*/
|
||||
override fun onActionConfirmed(action: Int, event: Int) {
|
||||
val adapter = adapter ?: return
|
||||
presenter.deleteCategories(adapter.deletedItems.map { it.category })
|
||||
snack = null
|
||||
}
|
||||
|
||||
fun confirmDelete() {
|
||||
val adapter = adapter ?: return
|
||||
presenter.deleteCategories(adapter.deletedItems.map { it.category })
|
||||
presenter.deleteCategory(adapter.deletedItems.map { it.category }.firstOrNull())
|
||||
adapter.confirmDeletion()
|
||||
snack = null
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,21 @@
|
||||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.InputType
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.util.view.getRound
|
||||
import kotlinx.android.synthetic.main.categories_item.image
|
||||
import kotlinx.android.synthetic.main.categories_item.reorder
|
||||
import kotlinx.android.synthetic.main.categories_item.title
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Companion.CREATE_CATEGORY_ORDER
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import kotlinx.android.synthetic.main.categories_item.*
|
||||
|
||||
/**
|
||||
* Holder used to display category items.
|
||||
@ -17,15 +26,14 @@ import kotlinx.android.synthetic.main.categories_item.title
|
||||
class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
init {
|
||||
// Create round letter image onclick to simulate long click
|
||||
image.setOnClickListener {
|
||||
// Simulate long click on this view to enter selection mode
|
||||
onLongClick(view)
|
||||
edit_button.setOnClickListener {
|
||||
submitChanges()
|
||||
}
|
||||
|
||||
setDragHandleView(reorder)
|
||||
}
|
||||
|
||||
var createCategory = false
|
||||
private var regularDrawable: Drawable? = null
|
||||
|
||||
/**
|
||||
* Binds this holder with the given category.
|
||||
*
|
||||
@ -34,11 +42,90 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
||||
fun bind(category: Category) {
|
||||
// Set capitalized title.
|
||||
title.text = category.name.capitalize()
|
||||
|
||||
// Update circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(category.name.take(1).toUpperCase(),false))
|
||||
edit_text.setOnEditorActionListener { _, actionId, _ ->
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
submitChanges()
|
||||
}
|
||||
true
|
||||
}
|
||||
createCategory = category.order == CREATE_CATEGORY_ORDER
|
||||
if (createCategory) {
|
||||
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.textColorHint))
|
||||
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
|
||||
.ic_add_white_24dp)
|
||||
edit_button.gone()
|
||||
image.gone()
|
||||
edit_text.setText("")
|
||||
edit_text.hint = title.text
|
||||
}
|
||||
else {
|
||||
title.setTextColor(ContextCompat.getColor(itemView.context, R.color.textColorPrimary))
|
||||
regularDrawable = ContextCompat.getDrawable(itemView.context, R.drawable
|
||||
.ic_reorder_grey_24dp)
|
||||
edit_button.visible()
|
||||
image.visible()
|
||||
edit_text.setText(title.text)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun isEditing(editing: Boolean) {
|
||||
itemView.isActivated = editing
|
||||
title.visibility = if (editing) View.INVISIBLE else View.VISIBLE
|
||||
edit_text.visibility = if (!editing) View.INVISIBLE else View.VISIBLE
|
||||
if (editing) {
|
||||
edit_text.inputType = InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
|
||||
edit_text.requestFocus()
|
||||
edit_text.selectAll()
|
||||
edit_button.setImageDrawable(ContextCompat.getDrawable(itemView.context, R.drawable.ic_check_white_24dp))
|
||||
edit_button.drawable.mutate().setTint(itemView.context.getResourceColor(R.attr.colorAccent))
|
||||
showKeyboard()
|
||||
if (!createCategory) {
|
||||
reorder.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
itemView.context, R.drawable.ic_delete_white_24dp
|
||||
)
|
||||
)
|
||||
reorder.setOnClickListener {
|
||||
adapter.categoryItemListener.onItemDelete(adapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!createCategory) {
|
||||
setDragHandleView(reorder)
|
||||
}
|
||||
else {
|
||||
reorder.setOnTouchListener { _, _ -> true}
|
||||
}
|
||||
edit_text.clearFocus()
|
||||
edit_button.setImageDrawable(ContextCompat.getDrawable(itemView.context, R.drawable.ic_edit_white_24dp))
|
||||
edit_button.drawable.mutate().setTint(ContextCompat.getColor(itemView.context, R
|
||||
.color.gray_button))
|
||||
reorder.setImageDrawable(regularDrawable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitChanges() {
|
||||
if (edit_text.visibility == View.VISIBLE ) {
|
||||
if (adapter.categoryItemListener
|
||||
.onCategoryRename(adapterPosition, edit_text.text.toString())) {
|
||||
isEditing(false)
|
||||
edit_text.inputType = InputType.TYPE_NULL
|
||||
if (!createCategory)
|
||||
title.text = edit_text.text.toString()
|
||||
}
|
||||
}
|
||||
else {
|
||||
itemView.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showKeyboard() {
|
||||
val inputMethodManager: InputMethodManager =
|
||||
itemView.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
inputMethodManager.showSoftInput(edit_text, WindowManager.LayoutParams
|
||||
.SOFT_INPUT_ADJUST_PAN)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,7 +135,7 @@ class CategoryHolder(view: View, val adapter: CategoryAdapter) : BaseFlexibleVie
|
||||
*/
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
adapter.onItemReleaseListener.onItemReleased(position)
|
||||
adapter.categoryItemListener.onItemReleased(position)
|
||||
}
|
||||
|
||||
}
|
@ -7,6 +7,7 @@ import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryPresenter.Companion.CREATE_CATEGORY_ORDER
|
||||
|
||||
/**
|
||||
* Category item for a recycler view.
|
||||
@ -16,7 +17,7 @@ class CategoryItem(val category: Category) : AbstractFlexibleItem<CategoryHolder
|
||||
/**
|
||||
* Whether this item is currently selected.
|
||||
*/
|
||||
var isSelected = false
|
||||
var isEditing = false
|
||||
|
||||
/**
|
||||
* Returns the layout resource for this item.
|
||||
@ -45,13 +46,14 @@ class CategoryItem(val category: Category) : AbstractFlexibleItem<CategoryHolder
|
||||
*/
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>, holder: CategoryHolder, position: Int, payloads: MutableList<Any>) {
|
||||
holder.bind(category)
|
||||
holder.isEditing(isEditing)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this item is draggable.
|
||||
*/
|
||||
override fun isDraggable(): Boolean {
|
||||
return true
|
||||
return category.order != CREATE_CATEGORY_ORDER && !isEditing
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
|
@ -1,11 +1,13 @@
|
||||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
@ -13,27 +15,41 @@ import uy.kohesive.injekt.api.get
|
||||
* Presenter of [CategoryController]. Used to manage the categories of the library.
|
||||
*/
|
||||
class CategoryPresenter(
|
||||
private val db: DatabaseHelper = Injekt.get()
|
||||
) : BasePresenter<CategoryController>() {
|
||||
private val controller: CategoryController,
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
preferences: PreferencesHelper = Injekt.get()
|
||||
) {
|
||||
|
||||
private val context = preferences.context
|
||||
|
||||
/**
|
||||
* List containing categories.
|
||||
*/
|
||||
private var categories: List<Category> = emptyList()
|
||||
private var categories: MutableList<Category> = mutableListOf()
|
||||
|
||||
/**
|
||||
* Called when the presenter is created.
|
||||
*
|
||||
* @param savedState The saved state of this presenter.
|
||||
*/
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
fun getCategories() {
|
||||
if (categories.isNotEmpty()) {
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
}
|
||||
GlobalScope.launch(Dispatchers.IO) {
|
||||
categories.clear()
|
||||
categories.add(newCategory())
|
||||
categories.addAll(db.getCategories().executeAsBlocking())
|
||||
val catItems = categories.map(::CategoryItem)
|
||||
withContext(Dispatchers.Main) {
|
||||
controller.setCategories(catItems)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
db.getCategories().asRxObservable()
|
||||
.doOnNext { categories = it }
|
||||
.map { it.map(::CategoryItem) }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeLatestCache(CategoryController::setCategories)
|
||||
private fun newCategory(): Category {
|
||||
val default = Category.create(context.getString(R.string.create_new_category))
|
||||
default.order = CREATE_CATEGORY_ORDER
|
||||
default.id = Int.MIN_VALUE
|
||||
return default
|
||||
}
|
||||
|
||||
/**
|
||||
@ -41,11 +57,11 @@ class CategoryPresenter(
|
||||
*
|
||||
* @param name The name of the category to create.
|
||||
*/
|
||||
fun createCategory(name: String) {
|
||||
fun createCategory(name: String): Boolean {
|
||||
// Do not allow duplicate categories.
|
||||
if (categoryExists(name)) {
|
||||
Observable.just(Unit).subscribeFirst({ view, _ -> view.onCategoryExistsError() })
|
||||
return
|
||||
controller.onCategoryExistsError()
|
||||
return false
|
||||
}
|
||||
|
||||
// Create category.
|
||||
@ -55,16 +71,25 @@ class CategoryPresenter(
|
||||
cat.order = categories.map { it.order + 1 }.max() ?: 0
|
||||
|
||||
// Insert into database.
|
||||
db.insertCategory(cat).asRxObservable().subscribe()
|
||||
|
||||
db.insertCategory(cat).executeAsBlocking()
|
||||
val cats = db.getCategories().executeAsBlocking()
|
||||
val newCat = cats.find { it.name == name } ?: return false
|
||||
categories.add(1, newCat)
|
||||
reorderCategories(categories)
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the given categories from the database.
|
||||
*
|
||||
* @param categories The list of categories to delete.
|
||||
* @param category The category to delete.
|
||||
*/
|
||||
fun deleteCategories(categories: List<Category>) {
|
||||
db.deleteCategories(categories).asRxObservable().subscribe()
|
||||
fun deleteCategory(category: Category?) {
|
||||
val safeCategory = category ?: return
|
||||
db.deleteCategory(safeCategory).executeAsBlocking()
|
||||
categories.remove(safeCategory)
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,10 +99,12 @@ class CategoryPresenter(
|
||||
*/
|
||||
fun reorderCategories(categories: List<Category>) {
|
||||
categories.forEachIndexed { i, category ->
|
||||
category.order = i
|
||||
if (category.order != CREATE_CATEGORY_ORDER)
|
||||
category.order = i - 1
|
||||
}
|
||||
|
||||
db.insertCategories(categories).asRxObservable().subscribe()
|
||||
db.insertCategories(categories.filter { it.order != CREATE_CATEGORY_ORDER }).executeAsBlocking()
|
||||
this.categories = categories.sortedBy { it.order }.toMutableList()
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -86,22 +113,29 @@ class CategoryPresenter(
|
||||
* @param category The category to rename.
|
||||
* @param name The new name of the category.
|
||||
*/
|
||||
fun renameCategory(category: Category, name: String) {
|
||||
fun renameCategory(category: Category, name: String): Boolean {
|
||||
// Do not allow duplicate categories.
|
||||
if (categoryExists(name)) {
|
||||
Observable.just(Unit).subscribeFirst({ view, _ -> view.onCategoryExistsError() })
|
||||
return
|
||||
controller.onCategoryExistsError()
|
||||
return false
|
||||
}
|
||||
|
||||
category.name = name
|
||||
db.insertCategory(category).asRxObservable().subscribe()
|
||||
db.insertCategory(category).executeAsBlocking()
|
||||
categories.find { it.id == category.id }?.name = name
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a category with the given name already exists.
|
||||
*/
|
||||
fun categoryExists(name: String): Boolean {
|
||||
private fun categoryExists(name: String): Boolean {
|
||||
return categories.any { it.name.equals(name, true) }
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CREATE_CATEGORY_ORDER = -2
|
||||
}
|
||||
|
||||
}
|
@ -44,13 +44,13 @@ class ExtensionHolder(view: View, override val adapter: ExtensionAdapter) :
|
||||
itemView.context.getString(R.string.ext_untrusted).toUpperCase()
|
||||
}
|
||||
|
||||
GlideApp.with(itemView.context).clear(image)
|
||||
GlideApp.with(itemView.context).clear(edit_button)
|
||||
if (extension is Extension.Available) {
|
||||
GlideApp.with(itemView.context)
|
||||
.load(extension.iconUrl)
|
||||
.into(image)
|
||||
.into(edit_button)
|
||||
} else {
|
||||
extension.getApplicationIcon(itemView.context)?.let { image.setImageDrawable(it) }
|
||||
extension.getApplicationIcon(itemView.context)?.let { edit_button.setImageDrawable(it) }
|
||||
}
|
||||
bindButton(item)
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
|
||||
import eu.kanade.tachiyomi.util.lang.chop
|
||||
import eu.kanade.tachiyomi.util.lang.removeArticles
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import kotlinx.coroutines.delay
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Calendar
|
||||
@ -28,9 +30,6 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
||||
*/
|
||||
private var mangas: List<LibraryItem> = emptyList()
|
||||
|
||||
val onItemReleaseListener: CategoryAdapter.OnItemReleaseListener = view
|
||||
|
||||
|
||||
/**
|
||||
* Listener called when an item of the list press start reading.
|
||||
*/
|
||||
@ -145,5 +144,6 @@ class LibraryCategoryAdapter(val view: LibraryCategoryView) :
|
||||
* Called when an item of the list is released.
|
||||
*/
|
||||
fun startReading(position: Int)
|
||||
fun onItemReleased(position: Int)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -18,7 +17,6 @@ import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
|
||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
|
||||
import eu.kanade.tachiyomi.util.lang.plusAssign
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
@ -40,8 +38,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
LibraryCategoryAdapter.LibraryListener,
|
||||
CategoryAdapter.OnItemReleaseListener {
|
||||
LibraryCategoryAdapter.LibraryListener {
|
||||
|
||||
/**
|
||||
* Preferences.
|
||||
@ -209,16 +206,12 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
*
|
||||
* @param event the event received.
|
||||
*/
|
||||
fun onNextLibraryManga(event: LibraryMangaEvent) {
|
||||
private fun onNextLibraryManga(event: LibraryMangaEvent) {
|
||||
// Get the manga list for this category.
|
||||
adapter.isLongPressDragEnabled = canDrag()
|
||||
val mangaForCategory = event.getMangaForCategory(category).orEmpty()
|
||||
|
||||
// Update the category with its manga.
|
||||
// if (!justDraggedAndDropped)
|
||||
adapter.setItems(mangaForCategory)
|
||||
// else
|
||||
// justDraggedAndDropped = false
|
||||
adapter.setItems(mangaForCategory)
|
||||
|
||||
swipe_refresh.isEnabled = !preferences.hideCategories().getOrDefault()
|
||||
|
||||
|
@ -31,9 +31,27 @@ class LibraryGridHolder(
|
||||
private val view: View,
|
||||
adapter: LibraryCategoryAdapter,
|
||||
var width:Int,
|
||||
var fixedSize: Boolean
|
||||
private var fixedSize: Boolean
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
init {
|
||||
play_layout.setOnClickListener { playButtonClicked() }
|
||||
if (fixedSize) {
|
||||
title.gone()
|
||||
subtitle.gone()
|
||||
}
|
||||
else {
|
||||
compact_title.gone()
|
||||
gradient.gone()
|
||||
val playLayout = play_layout.layoutParams as FrameLayout.LayoutParams
|
||||
val buttonLayout = play_button.layoutParams as FrameLayout.LayoutParams
|
||||
playLayout.gravity = Gravity.BOTTOM or Gravity.END
|
||||
buttonLayout.gravity = Gravity.BOTTOM or Gravity.END
|
||||
play_layout.layoutParams = playLayout
|
||||
play_button.layoutParams = buttonLayout
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method called from [LibraryCategoryAdapter.onBindViewHolder]. It updates the data for this
|
||||
* holder with the given manga.
|
||||
@ -44,6 +62,7 @@ class LibraryGridHolder(
|
||||
// Update the title and subtitle of the manga.
|
||||
title.text = item.manga.currentTitle()
|
||||
subtitle.text = item.manga.originalAuthor()?.trim()
|
||||
|
||||
if (!fixedSize) {
|
||||
title.viewTreeObserver.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
|
||||
override fun onPreDraw(): Boolean {
|
||||
@ -74,25 +93,13 @@ class LibraryGridHolder(
|
||||
0 -> if (item.manga.unread > 0) -1 else -2
|
||||
else -> -2
|
||||
},
|
||||
if (item.manga.source == LocalSource.ID) -2 else item.downloadCount)
|
||||
when {
|
||||
item.downloadCount == -1 -> -1
|
||||
item.manga.source == LocalSource.ID -> -2
|
||||
else -> item.downloadCount
|
||||
})
|
||||
play_layout.visibility = if (item.manga.unread > 0 && item.unreadType > -1)
|
||||
View.VISIBLE else View.GONE
|
||||
play_layout.setOnClickListener { playButtonClicked() }
|
||||
|
||||
if (fixedSize) {
|
||||
title.gone()
|
||||
subtitle.gone()
|
||||
}
|
||||
else {
|
||||
compact_title.gone()
|
||||
gradient.gone()
|
||||
val playLayout = play_layout.layoutParams as FrameLayout.LayoutParams
|
||||
val buttonLayout = play_button.layoutParams as FrameLayout.LayoutParams
|
||||
playLayout.gravity = Gravity.BOTTOM or Gravity.END
|
||||
buttonLayout.gravity = Gravity.BOTTOM or Gravity.END
|
||||
play_layout.layoutParams = playLayout
|
||||
play_button.layoutParams = buttonLayout
|
||||
}
|
||||
|
||||
// Update the cover.
|
||||
if (item.manga.thumbnail_url == null) GlideApp.with(view.context).clear(cover_thumbnail)
|
||||
|
@ -35,7 +35,7 @@ abstract class LibraryHolder(
|
||||
*/
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
(adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position)
|
||||
(adapter as? LibraryCategoryAdapter)?.libraryListener?.onItemReleased(position)
|
||||
}
|
||||
|
||||
protected fun convertColor(color: Int):String {
|
||||
|
@ -48,6 +48,7 @@ class LibraryItem(val manga: LibraryManga, private val libraryLayout: Preference
|
||||
val marginParams = card.layoutParams as ConstraintLayout.LayoutParams
|
||||
marginParams.bottomMargin = 6.dpToPx
|
||||
card.layoutParams = marginParams
|
||||
cover_thumbnail.maxHeight = Integer.MAX_VALUE
|
||||
constraint_layout.minHeight = 0
|
||||
cover_thumbnail.adjustViewBounds = false
|
||||
cover_thumbnail.layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, coverHeight)
|
||||
|
@ -38,8 +38,16 @@ class LibraryListHolder(
|
||||
title.text = item.manga.currentTitle()
|
||||
|
||||
badge_view.setUnreadDownload(
|
||||
if (item.unreadType == 1) item.manga.unread else (item.unreadType - 1),
|
||||
if (item.manga.source == LocalSource.ID) -2 else item.downloadCount)
|
||||
when (item.unreadType) {
|
||||
1 -> item.manga.unread
|
||||
0 -> if (item.manga.unread > 0) -1 else -2
|
||||
else -> -2
|
||||
},
|
||||
when {
|
||||
item.downloadCount == -1 -> -1
|
||||
item.manga.source == LocalSource.ID -> -2
|
||||
else -> item.downloadCount
|
||||
})
|
||||
|
||||
subtitle.text = item.manga.originalAuthor()?.trim()
|
||||
subtitle.visibility = if (!item.manga.originalAuthor().isNullOrBlank()) View.VISIBLE
|
||||
|
@ -1,6 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.main
|
||||
|
||||
import android.app.SearchManager
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.Color
|
||||
@ -11,6 +12,7 @@ import android.provider.Settings
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.webkit.WebView
|
||||
import android.widget.FrameLayout
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
@ -53,8 +55,11 @@ import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.updatePadding
|
||||
import eu.kanade.tachiyomi.util.view.updatePaddingRelative
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
import kotlinx.android.synthetic.main.main_activity.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
@ -166,6 +171,21 @@ open class MainActivity : BaseActivity(), DownloadServiceListener {
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
updateRecentsIcon()
|
||||
content.viewTreeObserver.addOnGlobalLayoutListener {
|
||||
val heightDiff: Int = content.rootView.height - content.height
|
||||
if (heightDiff > 200 &&
|
||||
window.attributes.softInputMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE) {
|
||||
//keyboard is open, hide layout
|
||||
navigationView.gone()
|
||||
} else if (navigationView.visibility == View.GONE) {
|
||||
//keyboard is hidden, show layout
|
||||
// use coroutine to delay so the bottom bar doesn't flash on top of the keyboard
|
||||
launchUI {
|
||||
navigationView.visible()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content.setOnApplyWindowInsetsListener { v, insets ->
|
||||
// if device doesn't support light nav bar
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
||||
|
@ -17,20 +17,13 @@ class MangaHolder(
|
||||
fun bind(item: MangaItem) {
|
||||
// Update the title of the manga.
|
||||
title.text = item.manga.currentTitle()
|
||||
|
||||
// Create thumbnail onclick to simulate long click
|
||||
cover_thumbnail.setOnClickListener {
|
||||
// Simulate long click on this view to enter selection mode
|
||||
onLongClick(itemView)
|
||||
}
|
||||
subtitle.text = item.manga.currentAuthor()?.trim()
|
||||
|
||||
// Update the cover.
|
||||
GlideApp.with(itemView.context).clear(cover_thumbnail)
|
||||
GlideApp.with(itemView.context)
|
||||
.load(item.manga)
|
||||
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
|
||||
// .centerCrop()
|
||||
// .circleCrop()
|
||||
.dontAnimate()
|
||||
.into(cover_thumbnail)
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.design.PreMigrationController
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationListController
|
||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
||||
import eu.kanade.tachiyomi.util.system.await
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.ui.migration.manga.process.MigrationProcedureConfig
|
||||
import eu.kanade.tachiyomi.util.view.RecyclerWindowInsetsListener
|
||||
import kotlinx.android.synthetic.main.migration_controller.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -91,12 +91,17 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
}
|
||||
adapter?.updateDataSet(state.sourcesWithManga)
|
||||
} else {
|
||||
val switching = title == resources?.getString(R.string.label_migration)
|
||||
title = state.selectedSource.toString()
|
||||
if (adapter !is MangaAdapter) {
|
||||
adapter = MangaAdapter(this)
|
||||
migration_recycler.adapter = adapter
|
||||
}
|
||||
adapter?.updateDataSet(state.mangaForSource)
|
||||
adapter?.updateDataSet(state.mangaForSource, true)
|
||||
/*if (switching) launchUI {
|
||||
migration_recycler.alpha = 0f
|
||||
migration_recycler.animate().alpha(1f).setStartDelay(100).setDuration(200).start()
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,7 +109,7 @@ class MigrationController : NucleusController<MigrationPresenter>(),
|
||||
val item = adapter?.getItem(position) ?: return false
|
||||
|
||||
if (item is MangaItem) {
|
||||
val controller = SearchController(item.manga)
|
||||
val controller = PreMigrationController.create(listOf(item.manga.id!!))
|
||||
controller.targetController = this
|
||||
|
||||
router.pushController(controller.withFadeTransaction())
|
||||
|
@ -20,7 +20,7 @@ class SourceHolder(view: View, override val adapter: SourceAdapter) :
|
||||
get() = card
|
||||
|
||||
init {
|
||||
source_latest.text = "Auto"
|
||||
source_latest.text = view.context.getString(R.string.action_auto)
|
||||
source_browse.setText(R.string.select)
|
||||
source_browse.setOnClickListener {
|
||||
adapter.selectClickListener?.onSelectClick(adapterPosition)
|
||||
@ -39,7 +39,7 @@ class SourceHolder(view: View, override val adapter: SourceAdapter) :
|
||||
|
||||
// Set circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false))
|
||||
edit_button.setImageDrawable(edit_button.getRound(source.name.take(1).toUpperCase(),false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,16 +24,16 @@ class MigrationSourceHolder(view: View, val adapter: MigrationSourceAdapter):
|
||||
title.text = sourceName
|
||||
// Update circle letter image.
|
||||
itemView.post {
|
||||
image.setImageDrawable(image.getRound(source.name.take(1).toUpperCase(),false))
|
||||
edit_button.setImageDrawable(edit_button.getRound(source.name.take(1).toUpperCase(),false))
|
||||
}
|
||||
|
||||
if(sourceEnabled) {
|
||||
title.alpha = 1.0f
|
||||
image.alpha = 1.0f
|
||||
edit_button.alpha = 1.0f
|
||||
title.paintFlags = title.paintFlags and STRIKE_THRU_TEXT_FLAG.inv()
|
||||
} else {
|
||||
title.alpha = DISABLED_ALPHA
|
||||
image.alpha = DISABLED_ALPHA
|
||||
edit_button.alpha = DISABLED_ALPHA
|
||||
title.paintFlags = title.paintFlags or STRIKE_THRU_TEXT_FLAG
|
||||
}
|
||||
}
|
||||
|
24
app/src/main/res/drawable/bordered_list_selector.xml
Normal file
24
app/src/main/res/drawable/bordered_list_selector.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:background="@color/red_error">
|
||||
<item
|
||||
android:bottom="1dp"
|
||||
android:left="-2dp"
|
||||
android:right="-2dp"
|
||||
android:top="1dp">
|
||||
<selector>
|
||||
<item android:state_activated="true">
|
||||
<shape
|
||||
android:right="8dp"
|
||||
android:shape="rectangle">
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/fullRippleColor" />
|
||||
|
||||
<solid android:color="#00FFFFFF" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</layer-list>
|
@ -9,26 +9,24 @@
|
||||
</item>
|
||||
<item>
|
||||
<selector>
|
||||
<item android:state_activated="false">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="16dp"/>
|
||||
<size
|
||||
android:height="32dp"
|
||||
android:width="32dp" />
|
||||
<solid android:color="@android:color/transparent"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_activated="true">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="16dp"/>
|
||||
<size
|
||||
android:height="32dp"
|
||||
android:width="32dp" />
|
||||
<solid android:color="?attr/colorAccent"/>
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_activated="false">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="16dp" />
|
||||
<size
|
||||
android:width="32dp"
|
||||
android:height="32dp" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
</item>
|
||||
<item android:state_activated="true">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="16dp" />
|
||||
<size
|
||||
android:width="32dp"
|
||||
android:height="32dp" />
|
||||
<solid android:color="?attr/colorAccent" />
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
</ripple>
|
@ -1,13 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout 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/manga_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
<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/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -156,5 +150,4 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Sample artist" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</FrameLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -13,7 +13,7 @@
|
||||
android:background="?attr/selectable_list_drawable">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:id="@+id/edit_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:padding="8dp"
|
||||
@ -33,7 +33,7 @@
|
||||
android:ellipsize="end"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/image"
|
||||
app:layout_constraintStart_toEndOf="@+id/edit_button"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/source_latest"
|
||||
tools:text="Source title"/>
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/cat_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="vertical">
|
||||
|
||||
@ -13,20 +13,13 @@
|
||||
android:layout_height="match_parent"
|
||||
android:choiceMode="multipleChoice"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/categories_item"
|
||||
/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
app:layout_anchor="@id/recycler"
|
||||
app:srcCompat="@drawable/ic_add_white_24dp"
|
||||
style="@style/Theme.Widget.FABFixed"/>
|
||||
tools:listitem="@layout/categories_item" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content" />
|
||||
android:visibility="gone" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@ -1,40 +1,78 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:background="?attr/selectable_list_drawable">
|
||||
android:background="@drawable/bordered_list_selector">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:clickable="true"
|
||||
android:paddingStart="@dimen/material_component_lists_icon_left_padding"
|
||||
android:paddingEnd="0dp"
|
||||
tools:src="@mipmap/ic_launcher_round"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/material_component_lists_text_left_padding"
|
||||
android:layout_marginEnd="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading"
|
||||
tools:text="Title"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/reorder"
|
||||
android:layout_width="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:scaleType="center"
|
||||
android:layout_gravity="end"
|
||||
app:srcCompat="@drawable/ic_reorder_grey_24dp"
|
||||
android:tint="?android:attr/textColorPrimary"/>
|
||||
android:tint="?android:attr/textColorPrimary"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_reorder_grey_24dp" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="match_parent"
|
||||
android:tint="@color/gray_button"
|
||||
android:src="@drawable/ic_label_outline_white_24dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginEnd="6dp"
|
||||
app:layout_constraintStart_toEndOf="@+id/reorder"
|
||||
app:layout_constraintEnd_toStartOf="@+id/title"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/edit_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:src="@drawable/ic_edit_white_24dp"
|
||||
android:tint="@color/gray_button"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/edit_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@null"
|
||||
android:imeOptions="actionDone"
|
||||
android:inputType="none"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toEndOf="@id/title"
|
||||
app:layout_constraintTop_toTopOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/title"
|
||||
app:layout_constraintStart_toStartOf="@id/title"
|
||||
tools:text="Title" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|start"
|
||||
android:layout_marginStart="3dp"
|
||||
android:inputType="none"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/textColorPrimary"
|
||||
android:textSize="16sp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/edit_button"
|
||||
app:layout_constraintStart_toEndOf="@+id/image"
|
||||
tools:text="Title" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -13,7 +13,7 @@
|
||||
android:background="?attr/selectable_list_drawable">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:id="@+id/edit_button"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:padding="12dp"
|
||||
@ -33,7 +33,7 @@
|
||||
android:maxLines="1"
|
||||
android:textAppearance="@style/TextAppearance.Regular.SubHeading"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintStart_toEndOf="@id/image"
|
||||
app:layout_constraintStart_toEndOf="@id/edit_button"
|
||||
app:layout_constraintEnd_toStartOf="@id/ext_button"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/lang"
|
||||
@ -47,7 +47,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
app:layout_constraintStart_toEndOf="@id/image"
|
||||
app:layout_constraintStart_toEndOf="@id/edit_button"
|
||||
app:layout_constraintTop_toBottomOf="@+id/ext_title"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:text="English"
|
||||
|
@ -8,7 +8,7 @@
|
||||
android:background="?attr/selectable_list_drawable">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image"
|
||||
android:id="@+id/edit_button"
|
||||
android:layout_width="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:layout_height="@dimen/material_component_lists_single_line_with_avatar_height"
|
||||
android:paddingLeft="@dimen/material_component_lists_icon_left_padding"
|
||||
|
@ -175,6 +175,7 @@
|
||||
<string name="portrait">Portrait</string>
|
||||
<string name="landscape">Landscape</string>
|
||||
<string name="default_columns">Default</string>
|
||||
<string name="create_new_category">Create new category</string>
|
||||
|
||||
<string name="pref_category_library_update">Updates</string>
|
||||
<string name="pref_library_update_interval">Library update frequency</string>
|
||||
@ -412,6 +413,9 @@
|
||||
<string name="category_already_in_queue">%1$s is already in queue</string>
|
||||
<string name="local_source_badge">Local</string>
|
||||
<string name="confirm_manga_deletion">Remove from library?</string>
|
||||
<string name="confirm_category_deletion">Delete category?</string>
|
||||
<string name="confirm_category_deletion_message">Manga in this category will moved into the
|
||||
default category.</string>
|
||||
<plurals name="unread_count">
|
||||
<item quantity="one">New chapter</item>
|
||||
<item quantity="other">%d unread</item>
|
||||
@ -526,7 +530,7 @@
|
||||
|
||||
<!-- Category activity -->
|
||||
<string name="error_category_exists">A category with this name already exists!</string>
|
||||
<string name="snack_categories_deleted">Categories deleted</string>
|
||||
<string name="snack_category_deleted">Category deleted</string>
|
||||
|
||||
<!-- Dialog option with checkbox view -->
|
||||
<string name="dialog_with_checkbox_remove_description">This will remove the read date of this chapter. Are you sure?</string>
|
||||
@ -649,5 +653,6 @@
|
||||
<string name="pre_migration_skip_toast">To show this screen again, go to Settings -> Library.</string>
|
||||
<string name="reset_tags">Reset Tags</string>
|
||||
<string name="display_as">Display as</string>
|
||||
<string name="action_auto">Auto</string>
|
||||
|
||||
</resources>
|
||||
|
Loading…
Reference in New Issue
Block a user