Fixed being able to drag n drop the a manga into a category that already has it

Sometimes you have to save users from themselves
Added checkbox to also default  switching category sorting when moving manga
pull/3117/head
Jay 5 years ago
parent 3044dca9bb
commit c3a10692a1

@ -137,6 +137,8 @@ object PreferenceKeys {
const val refreshCoversToo = "refresh_covers_too" const val refreshCoversToo = "refresh_covers_too"
const val keepCatSort = "keep_cat_sort"
@Deprecated("Use the preferences of the source") @Deprecated("Use the preferences of the source")
fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId" fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId"

@ -239,6 +239,8 @@ class PreferencesHelper(val context: Context) {
fun unreadBadgeType() = rxPrefs.getInteger("unread_badge_type", 1) fun unreadBadgeType() = rxPrefs.getInteger("unread_badge_type", 1)
fun keepCatSort() = rxPrefs.getInteger(Keys.keepCatSort, 0)
fun upgradeFilters() { fun upgradeFilters() {
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault() val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault() val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()

@ -67,6 +67,17 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
else false } else false }
} }
/**
* Returns the position in the adapter for the given manga.
*
* @param manga the manga to find.
*/
fun allIndexOf(manga: Manga): List<Int> {
return currentItems.mapIndexedNotNull { index, it ->
if (it is LibraryItem && it.manga.id == manga.id) index
else null }
}
fun performFilter() { fun performFilter() {
val s = getFilter(String::class.java) val s = getFilter(String::class.java)
if (s.isNullOrBlank()) { if (s.isNullOrBlank()) {
@ -161,6 +172,6 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
fun onItemReleased(position: Int) fun onItemReleased(position: Int)
fun canDrag(): Boolean fun canDrag(): Boolean
fun updateCategory(catId: Int): Boolean fun updateCategory(catId: Int): Boolean
fun sortCategory(catId: Int, sortBy: Int): String fun sortCategory(catId: Int, sortBy: Int)
} }
} }

@ -221,7 +221,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
val position = adapter.indexOf(manga) val position = adapter.indexOf(manga)
if (position != -1 && !adapter.isSelected(position)) { if (position != -1 && !adapter.isSelected(position)) {
adapter.toggleSelection(position) adapter.toggleSelection(position)
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() (recycler.findViewHolderForAdapterPosition(position) as? LibraryHolder)?.toggleActivation()
} }
} }
} }
@ -272,7 +272,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
val position = adapter.indexOf(manga) val position = adapter.indexOf(manga)
if (position != -1) { if (position != -1) {
adapter.toggleSelection(position) adapter.toggleSelection(position)
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() (recycler.findViewHolderForAdapterPosition(position) as? LibraryHolder)?.toggleActivation()
} }
} }
@ -320,9 +320,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
lastClickPosition = position lastClickPosition = position
} }
override fun onItemMove(fromPosition: Int, toPosition: Int) { override fun onItemMove(fromPosition: Int, toPosition: Int) { }
}
override fun onItemReleased(position: Int) { override fun onItemReleased(position: Int) {
if (adapter.selectedItemCount == 0) saveDragSort() if (adapter.selectedItemCount == 0) saveDragSort()
@ -395,8 +393,5 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
return true return true
} }
override fun sortCategory(catId: Int, sortBy: Int): String { override fun sortCategory(catId: Int, sortBy: Int) { }
return ""
}
} }

@ -446,7 +446,7 @@ open class LibraryController(
/** /**
* Destroys the action mode. * Destroys the action mode.
*/ */
private fun destroyActionModeIfNeeded() { protected fun destroyActionModeIfNeeded() {
actionMode?.finish() actionMode?.finish()
} }

@ -131,12 +131,12 @@ class LibraryItem(val manga: LibraryManga,
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
if (other is LibraryItem) { if (other is LibraryItem) {
return manga.id == other.manga.id return manga.id == other.manga.id && manga.category == other.manga.category
} }
return false return false
} }
override fun hashCode(): Int { override fun hashCode(): Int {
return manga.id!!.hashCode() return (manga.id!! + (manga.category shl 50).toLong()).hashCode() //!!.hashCode()
} }
} }

@ -17,10 +17,13 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.checkbox.checkBoxPrompt
import com.afollestad.materialdialogs.checkbox.isCheckPromptChecked
import com.bluelinelabs.conductor.ControllerChangeHandler import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType import com.bluelinelabs.conductor.ControllerChangeType
import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.SelectableAdapter import eu.davidea.flexibleadapter.SelectableAdapter
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.LibraryManga import eu.kanade.tachiyomi.data.database.models.LibraryManga
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
@ -58,6 +61,9 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
private var spinnerAdapter: SpinnerAdapter? = null private var spinnerAdapter: SpinnerAdapter? = null
private var lastItemPostion:Int? = null
private var lastItem:IFlexible<*>? = null
/** /**
* Recycler view of the list of manga. * Recycler view of the list of manga.
*/ */
@ -261,7 +267,7 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
override fun setSelection(manga: Manga, selected: Boolean) { override fun setSelection(manga: Manga, selected: Boolean) {
if (selected) { if (selected) {
if (selectedMangas.add(manga)) { if (selectedMangas.add(manga)) {
val position = adapter.indexOf(manga) val positions = adapter.allIndexOf(manga)
if (adapter.mode != SelectableAdapter.Mode.MULTI) { if (adapter.mode != SelectableAdapter.Mode.MULTI) {
adapter.mode = SelectableAdapter.Mode.MULTI adapter.mode = SelectableAdapter.Mode.MULTI
} }
@ -269,19 +275,23 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
delay(100) delay(100)
adapter.isLongPressDragEnabled = false adapter.isLongPressDragEnabled = false
} }
adapter.toggleSelection(position) positions.forEach { position ->
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() adapter.addSelection(position)
(recycler.findViewHolderForAdapterPosition(position) as? LibraryHolder)?.toggleActivation()
}
} }
} else { } else {
if (selectedMangas.remove(manga)) { if (selectedMangas.remove(manga)) {
val position = adapter.indexOf(manga) val positions = adapter.allIndexOf(manga)
lastClickPosition = -1 lastClickPosition = -1
if (selectedMangas.isEmpty()) { if (selectedMangas.isEmpty()) {
adapter.mode = SelectableAdapter.Mode.SINGLE adapter.mode = SelectableAdapter.Mode.SINGLE
adapter.isLongPressDragEnabled = canDrag() adapter.isLongPressDragEnabled = canDrag()
} }
adapter.toggleSelection(position) positions.forEach { position ->
(recycler.findViewHolderForItemId(manga.id!!) as? LibraryHolder)?.toggleActivation() adapter.removeSelection(position)
(recycler.findViewHolderForAdapterPosition(position) as? LibraryHolder)?.toggleActivation()
}
} }
} }
} }
@ -358,7 +368,20 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) { override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
val position = viewHolder?.adapterPosition ?: return val position = viewHolder?.adapterPosition ?: return
if (actionState == 2) onItemLongClick(position) if (actionState == 2) {
if (lastItemPostion != null && position != lastItemPostion
&& lastItem == adapter.getItem(position)) {
// because for whatever reason you can re
adapter.removeSelection(position)
(recycler.findViewHolderForAdapterPosition(position) as? LibraryHolder)?.toggleActivation()
adapter.moveItem(position, lastItemPostion!!)
}
else {
lastItem = adapter.getItem(position)
lastItemPostion = position
onItemLongClick(position)
}
}
} }
override fun onUpdateManga(manga: LibraryManga) { override fun onUpdateManga(manga: LibraryManga) {
@ -378,10 +401,17 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
invalidateActionMode() invalidateActionMode()
} }
override fun onItemMove(fromPosition: Int, toPosition: Int) { } override fun onItemMove(fromPosition: Int, toPosition: Int) {
if (lastItemPostion == toPosition)
lastItemPostion = null
}
override fun onItemReleased(position: Int) { override fun onItemReleased(position: Int) {
if (adapter.selectedItemCount > 0) return if (adapter.selectedItemCount > 0) {
lastItemPostion = null
return
}
destroyActionModeIfNeeded()
val item = adapter.getItem(position) as? LibraryItem ?: return val item = adapter.getItem(position) as? LibraryItem ?: return
val newHeader = adapter.getSectionHeader(position) as? LibraryHeaderItem val newHeader = adapter.getSectionHeader(position) as? LibraryHeaderItem
val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position)) val libraryItems = adapter.getSectionItems(adapter.getSectionHeader(position))
@ -390,35 +420,58 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
if (newHeader?.category?.id == item.manga.category) { if (newHeader?.category?.id == item.manga.category) {
presenter.rearrangeCategory(item.manga.category, mangaIds) presenter.rearrangeCategory(item.manga.category, mangaIds)
} else { } else {
if (presenter.mangaIsInCategory(item.manga, newHeader?.category?.id)) {
adapter.moveItem(position, lastItemPostion!!)
snack = snackbar_layout?.snack(R.string.already_in_category)
return
}
if (newHeader?.category?.mangaSort == null) { if (newHeader?.category?.mangaSort == null) {
presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds, true) presenter.moveMangaToCategory(item, newHeader?.category?.id, mangaIds, true)
} else { } else {
val keepCatSort = preferences.keepCatSort().getOrDefault()
if (keepCatSort == 0) {
MaterialDialog(activity!!).message(R.string.switch_to_dnd) MaterialDialog(activity!!).message(R.string.switch_to_dnd)
.positiveButton(R.string.action_switch) { .positiveButton(R.string.action_switch) {
presenter.moveMangaToCategory(item, newHeader.category.id, mangaIds, true) presenter.moveMangaToCategory(
}.negativeButton( item, newHeader.category.id, mangaIds, true
)
if (it.isCheckPromptChecked()) preferences.keepCatSort().set(2)
}
.checkBoxPrompt(R.string.remember_choice) {}
.negativeButton(
text = resources?.getString( text = resources?.getString(
R.string.keep_current_sort, R.string.keep_current_sort,
resources!!.getString(newHeader.category.sortRes()).toLowerCase resources!!.getString(newHeader.category.sortRes()).toLowerCase(
(Locale.getDefault()) Locale.getDefault()
)
) )
) { ) {
presenter.moveMangaToCategory( presenter.moveMangaToCategory(
item, newHeader.category.id, mangaIds, false item, newHeader.category.id, mangaIds, false
) )
if (it.isCheckPromptChecked()) preferences.keepCatSort().set(1)
}.cancelOnTouchOutside(false).show()
}
else {
presenter.moveMangaToCategory(
item, newHeader.category.id, mangaIds, keepCatSort == 2
)
} }
.cancelOnTouchOutside(false)
.show()
} }
} }
lastItemPostion = null
} }
override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean { override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean {
if (adapter.selectedItemCount > 1) //if (adapter.selectedItemCount > 1)
return false // return false
if (adapter.isSelected(fromPosition)) if (adapter.isSelected(fromPosition))
toggleSelection(fromPosition) toggleSelection(fromPosition)
return true val item = adapter.getItem(fromPosition) as? LibraryItem ?: return false
val newHeader = adapter.getSectionHeader(toPosition) as? LibraryHeaderItem
//if (adapter.getItem(toPosition) is LibraryHeaderItem) return false
return newHeader?.category?.id == item.manga.category ||
!presenter.mangaIsInCategory(item.manga, newHeader?.category?.id)
} }
override fun updateCategory(catId: Int): Boolean { override fun updateCategory(catId: Int): Boolean {
@ -438,8 +491,7 @@ class LibraryListController(bundle: Bundle? = null) : LibraryController(bundle),
return true return true
} }
override fun sortCategory(catId: Int, sortBy: Int): String { override fun sortCategory(catId: Int, sortBy: Int) {
presenter.sortCategory(catId, sortBy) presenter.sortCategory(catId, sortBy)
return ""
} }
} }

@ -843,6 +843,11 @@ class LibraryPresenter(
} }
} }
fun mangaIsInCategory(manga: LibraryManga, catId: Int?): Boolean {
val categories = db.getCategoriesForManga(manga).executeAsBlocking().map { it.id }
return catId in categories
}
private companion object { private companion object {
var currentLibrary:Library? = null var currentLibrary:Library? = null
} }

@ -1,9 +1,18 @@
package eu.kanade.tachiyomi.ui.setting package eu.kanade.tachiyomi.ui.setting
import android.app.Activity import android.app.Activity
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import androidx.core.graphics.drawable.DrawableCompat import androidx.core.graphics.drawable.DrawableCompat
import androidx.preference.* import androidx.preference.CheckBoxPreference
import androidx.preference.DialogPreference
import androidx.preference.DropDownPreference
import androidx.preference.EditTextPreference
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceGroup
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import eu.kanade.tachiyomi.widget.preference.ExtensionPreference import eu.kanade.tachiyomi.widget.preference.ExtensionPreference
import eu.kanade.tachiyomi.widget.preference.IntListMatPreference import eu.kanade.tachiyomi.widget.preference.IntListMatPreference
import eu.kanade.tachiyomi.widget.preference.ListMatPreference import eu.kanade.tachiyomi.widget.preference.ListMatPreference
@ -38,6 +47,11 @@ inline fun PreferenceGroup.editTextPreference(block: (@DSL EditTextPreference).(
return initThenAdd(EditTextPreference(context), block).also(::initDialog) return initThenAdd(EditTextPreference(context), block).also(::initDialog)
} }
inline fun PreferenceGroup.dropDownPreference(block: (@DSL DropDownPreference).() -> Unit):
DropDownPreference {
return initThenAdd(DropDownPreference(context), block).also(::initDialog)
}
inline fun PreferenceGroup.listPreference(activity: Activity?, block: (@DSL ListMatPreference).() inline fun PreferenceGroup.listPreference(activity: Activity?, block: (@DSL ListMatPreference).()
-> Unit): -> Unit):
ListMatPreference { ListMatPreference {

@ -182,6 +182,18 @@ class SettingsLibraryController : SettingsController() {
true true
} }
} }
intListPreference(activity) {
titleRes = R.string.pref_keep_category_sorting
key = Keys.keepCatSort
summaryRes = R.string.pref_keep_category_sorting_summary
entries = listOf(
context.getString(R.string.always_ask),
context.getString(R.string.option_keep_category_sort),
context.getString(R.string.option_switch_to_dnd)
)
entryRange = 0..2
defaultValue = 0
}
} }
if (preferences.skipPreMigration().getOrDefault() || preferences.migrationSources().getOrDefault().isNotEmpty()) { if (preferences.skipPreMigration().getOrDefault() || preferences.migrationSources().getOrDefault().isNotEmpty()) {
preferenceCategory { preferenceCategory {

@ -3,15 +3,9 @@ package eu.kanade.tachiyomi.widget.preference
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.util.AttributeSet import android.util.AttributeSet
import androidx.preference.Preference
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.list.listItemsSingleChoice import com.afollestad.materialdialogs.list.listItemsSingleChoice
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.ui.setting.defaultValue
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
class IntListMatPreference @JvmOverloads constructor(activity: Activity?, context: Context, class IntListMatPreference @JvmOverloads constructor(activity: Activity?, context: Context,
attrs: attrs:
@ -33,6 +27,7 @@ AttributeSet? =
defValue = defaultValue as? Int ?: defValue defValue = defaultValue as? Int ?: defValue
} }
override fun getSummary(): CharSequence { override fun getSummary(): CharSequence {
if (key == null || useCustomSummary) return super.getSummary()
val index = entryValues.indexOf(prefs.getInt(key, defValue).getOrDefault()) val index = entryValues.indexOf(prefs.getInt(key, defValue).getOrDefault())
return if (entries.isEmpty() || index == -1) "" return if (entries.isEmpty() || index == -1) ""
else entries[index] else entries[index]
@ -46,6 +41,7 @@ AttributeSet? =
initialSelection = default) { initialSelection = default) {
_, pos, _ -> _, pos, _ ->
val value = entryValues[pos] val value = entryValues[pos]
if (key != null)
prefs.getInt(key, defValue).set(value) prefs.getInt(key, defValue).set(value)
callChangeListener(value) callChangeListener(value)
this@IntListMatPreference.summary = this@IntListMatPreference.summary this@IntListMatPreference.summary = this@IntListMatPreference.summary

@ -6,9 +6,7 @@ import android.util.AttributeSet
import androidx.preference.Preference import androidx.preference.Preference
import com.afollestad.materialdialogs.MaterialDialog import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.callbacks.onDismiss import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.list.listItemsSingleChoice
import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.data.preference.getOrDefault
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -18,9 +16,19 @@ open class MatPreference @JvmOverloads constructor(val activity: Activity?, cont
null) : null) :
Preference(context, attrs) { Preference(context, attrs) {
protected var useCustomSummary = false
protected val prefs: PreferencesHelper = Injekt.get() protected val prefs: PreferencesHelper = Injekt.get()
private var isShowing = false private var isShowing = false
override fun setSummary(summaryResId: Int) {
useCustomSummary = true
super.setSummary(summaryResId)
}
override fun setSummary(summary: CharSequence?) {
useCustomSummary = true
super.setSummary(summary)
}
override fun onClick() { override fun onClick() {
if (!isShowing) if (!isShowing)
dialog().apply { dialog().apply {

@ -226,6 +226,13 @@
when updating library (overwrites local covers)</string> when updating library (overwrites local covers)</string>
<string name="pref_category_library_migration">Migration</string> <string name="pref_category_library_migration">Migration</string>
<string name="pref_keep_category_sorting">Change category sorting when moving</string>
<string name="pref_keep_category_sorting_summary">In single list mode, should dragging and
dropping an entry into a new category change the category\'s sorting to drag &amp;
drop?</string>
<string name="always_ask">Always ask</string>
<string name="option_keep_category_sort">Keep current sorting method</string>
<string name="option_switch_to_dnd">Switch to Drag &amp; Drop</string>
<!-- Extension section --> <!-- Extension section -->
@ -430,6 +437,8 @@
<item quantity="one">1 downloaded</item> <item quantity="one">1 downloaded</item>
<item quantity="other">%d downloaded</item> <item quantity="other">%d downloaded</item>
</plurals> </plurals>
<string name="remember_choice">Remember this choice</string>
<string name="already_in_category">Manga already in category</string>
<!-- Catalogue fragment --> <!-- Catalogue fragment -->
<string name="source_search_options">Search filters</string> <string name="source_search_options">Search filters</string>

Loading…
Cancel
Save