mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-09-10 21:03:13 +02:00
Drag & Drop Sorting in Library
This commit is contained in:
parent
5261864aba
commit
b872ab837a
@ -18,7 +18,7 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
/**
|
||||
* Version of the database.
|
||||
*/
|
||||
const val DATABASE_VERSION = 9
|
||||
const val DATABASE_VERSION = 10
|
||||
}
|
||||
|
||||
override fun onCreate(db: SupportSQLiteDatabase) = with(db) {
|
||||
@ -70,6 +70,9 @@ class DbOpenCallback : SupportSQLiteOpenHelper.Callback(DATABASE_VERSION) {
|
||||
if (oldVersion < 9) {
|
||||
db.execSQL(MangaTable.addHideTitle)
|
||||
}
|
||||
if (oldVersion < 10) {
|
||||
db.execSQL(CategoryTable.addMangaOrder)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigure(db: SupportSQLiteDatabase) {
|
||||
|
@ -13,6 +13,7 @@ import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.models.CategoryImpl
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_FLAGS
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_ID
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_MANGA_ORDER
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_NAME
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_ORDER
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.TABLE
|
||||
@ -40,6 +41,9 @@ class CategoryPutResolver : DefaultPutResolver<Category>() {
|
||||
put(COL_NAME, obj.name)
|
||||
put(COL_ORDER, obj.order)
|
||||
put(COL_FLAGS, obj.flags)
|
||||
val orderString = obj.mangaOrder.joinToString("/")
|
||||
put(COL_MANGA_ORDER, orderString)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +54,9 @@ class CategoryGetResolver : DefaultGetResolver<Category>() {
|
||||
name = cursor.getString(cursor.getColumnIndex(COL_NAME))
|
||||
order = cursor.getInt(cursor.getColumnIndex(COL_ORDER))
|
||||
flags = cursor.getInt(cursor.getColumnIndex(COL_FLAGS))
|
||||
|
||||
val orderString = cursor.getString(cursor.getColumnIndex(COL_MANGA_ORDER))
|
||||
mangaOrder = orderString?.split("/")?.mapNotNull { it.toLongOrNull() } ?: emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,8 @@ interface Category : Serializable {
|
||||
|
||||
var flags: Int
|
||||
|
||||
var mangaOrder:List<Long>
|
||||
|
||||
val nameLower: String
|
||||
get() = name.toLowerCase()
|
||||
|
||||
|
@ -10,6 +10,8 @@ class CategoryImpl : Category {
|
||||
|
||||
override var flags: Int = 0
|
||||
|
||||
override var mangaOrder: List<Long> = emptyList()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other == null || javaClass != other.javaClass) return false
|
||||
|
@ -12,12 +12,18 @@ object CategoryTable {
|
||||
|
||||
const val COL_FLAGS = "flags"
|
||||
|
||||
const val COL_MANGA_ORDER = "manga_order"
|
||||
|
||||
val createTableQuery: String
|
||||
get() = """CREATE TABLE $TABLE(
|
||||
$COL_ID INTEGER NOT NULL PRIMARY KEY,
|
||||
$COL_NAME TEXT NOT NULL,
|
||||
$COL_ORDER INTEGER NOT NULL,
|
||||
$COL_FLAGS INTEGER NOT NULL
|
||||
$COL_FLAGS INTEGER NOT NULL,
|
||||
$COL_MANGA_ORDER TEXT NOT NULL
|
||||
)"""
|
||||
|
||||
|
||||
val addMangaOrder: String
|
||||
get() = "ALTER TABLE $TABLE ADD COLUMN $COL_MANGA_ORDER TEXT"
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import androidx.preference.PreferenceManager
|
||||
import com.f2prateek.rx.preferences.Preference
|
||||
import com.f2prateek.rx.preferences.RxSharedPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import java.io.File
|
||||
@ -198,6 +199,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun skipPreMigration() = rxPrefs.getBoolean(Keys.skipPreMigration, false)
|
||||
|
||||
fun defaultMangaOrder() = rxPrefs.getString("default_manga_order", "")
|
||||
|
||||
fun upgradeFilters() {
|
||||
val filterDl = rxPrefs.getBoolean(Keys.filterDownloaded, false).getOrDefault()
|
||||
val filterUn = rxPrefs.getBoolean(Keys.filterUnread, false).getOrDefault()
|
||||
|
@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.ui.library
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryAdapter
|
||||
|
||||
/**
|
||||
* Adapter storing a list of manga in a certain category.
|
||||
@ -18,6 +20,8 @@ class LibraryCategoryAdapter(view: LibraryCategoryView) :
|
||||
*/
|
||||
private var mangas: List<LibraryItem> = emptyList()
|
||||
|
||||
val onItemReleaseListener: CategoryAdapter.OnItemReleaseListener = view
|
||||
|
||||
/**
|
||||
* Sets a list of manga in the adapter.
|
||||
*
|
||||
|
@ -7,15 +7,16 @@ import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.SelectableAdapter
|
||||
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.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.*
|
||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||
import kotlinx.android.synthetic.main.library_category.view.*
|
||||
@ -28,7 +29,9 @@ import uy.kohesive.injekt.injectLazy
|
||||
class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
FrameLayout(context, attrs),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnItemLongClickListener {
|
||||
FlexibleAdapter.OnItemLongClickListener,
|
||||
FlexibleAdapter.OnItemMoveListener,
|
||||
CategoryAdapter.OnItemReleaseListener {
|
||||
|
||||
/**
|
||||
* Preferences.
|
||||
@ -117,6 +120,8 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
} else {
|
||||
SelectableAdapter.Mode.SINGLE
|
||||
}
|
||||
val sortingMode = preferences.librarySortingMode().getOrDefault()
|
||||
adapter.isLongPressDragEnabled = sortingMode == LibrarySort.DRAG_AND_DROP
|
||||
|
||||
subscriptions += controller.searchRelay
|
||||
.doOnNext { adapter.setFilter(it) }
|
||||
@ -138,6 +143,27 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
controller.invalidateActionMode()
|
||||
}
|
||||
}
|
||||
|
||||
subscriptions += controller.reorganizeRelay
|
||||
.subscribe {
|
||||
if (it.first == category.id) {
|
||||
var items = when (it.second) {
|
||||
1, 2 -> adapter.currentItems.sortedBy {
|
||||
if (preferences.removeArticles().getOrDefault())
|
||||
it.manga.title.removeArticles()
|
||||
else
|
||||
it.manga.title
|
||||
}
|
||||
3, 4 -> adapter.currentItems.sortedBy { it.manga.last_update }
|
||||
else -> adapter.currentItems.sortedBy { it.manga.title }
|
||||
}
|
||||
if (it.second % 2 == 0)
|
||||
items = items.reversed()
|
||||
adapter.setItems(items)
|
||||
adapter.notifyDataSetChanged()
|
||||
onItemReleased(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onRecycle() {
|
||||
@ -158,8 +184,18 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
*/
|
||||
fun onNextLibraryManga(event: LibraryMangaEvent) {
|
||||
// Get the manga list for this category.
|
||||
val mangaForCategory = event.getMangaForCategory(category).orEmpty()
|
||||
|
||||
|
||||
val sortingMode = preferences.librarySortingMode().getOrDefault()
|
||||
adapter.isLongPressDragEnabled = sortingMode == LibrarySort.DRAG_AND_DROP
|
||||
var mangaForCategory = event.getMangaForCategory(category).orEmpty()
|
||||
if (sortingMode == LibrarySort.DRAG_AND_DROP) {
|
||||
if (category.name == "Default")
|
||||
category.mangaOrder = preferences.defaultMangaOrder().getOrDefault().split("/")
|
||||
.mapNotNull { it.toLongOrNull() }
|
||||
mangaForCategory = mangaForCategory.sortedBy { category.mangaOrder.indexOf(it.manga
|
||||
.id) }
|
||||
}
|
||||
// Update the category with its manga.
|
||||
adapter.setItems(mangaForCategory)
|
||||
|
||||
@ -185,6 +221,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
is LibrarySelectionEvent.Selected -> {
|
||||
if (adapter.mode != SelectableAdapter.Mode.MULTI) {
|
||||
adapter.mode = SelectableAdapter.Mode.MULTI
|
||||
adapter.isLongPressDragEnabled = false
|
||||
}
|
||||
findAndToggleSelection(event.manga)
|
||||
}
|
||||
@ -193,12 +230,16 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
if (adapter.indexOf(event.manga) != -1) lastClickPosition = -1
|
||||
if (controller.selectedMangas.isEmpty()) {
|
||||
adapter.mode = SelectableAdapter.Mode.SINGLE
|
||||
adapter.isLongPressDragEnabled = preferences.librarySortingMode()
|
||||
.getOrDefault() == LibrarySort.DRAG_AND_DROP
|
||||
}
|
||||
}
|
||||
is LibrarySelectionEvent.Cleared -> {
|
||||
adapter.mode = SelectableAdapter.Mode.SINGLE
|
||||
adapter.clearSelection()
|
||||
lastClickPosition = -1
|
||||
adapter.isLongPressDragEnabled = preferences.librarySortingMode()
|
||||
.getOrDefault() == LibrarySort.DRAG_AND_DROP
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,6 +290,7 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
*/
|
||||
override fun onItemLongClick(position: Int) {
|
||||
controller.createActionModeIfNeeded()
|
||||
adapter.isLongPressDragEnabled = false
|
||||
when {
|
||||
lastClickPosition == -1 -> setSelection(position)
|
||||
lastClickPosition > position -> for (i in position until lastClickPosition)
|
||||
@ -260,6 +302,36 @@ class LibraryCategoryView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
lastClickPosition = position
|
||||
}
|
||||
|
||||
override fun onItemMove(fromPosition: Int, toPosition: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun onItemReleased(position: Int) {
|
||||
if (adapter.selectedItemCount == 0) {
|
||||
val mangaIds = adapter.currentItems.mapNotNull { it.manga.id }
|
||||
category.mangaOrder = mangaIds
|
||||
val db: DatabaseHelper by injectLazy()
|
||||
if (category.name == "Default")
|
||||
preferences.defaultMangaOrder().set(mangaIds.joinToString("/"))
|
||||
else
|
||||
db.insertCategory(category).asRxObservable().subscribe()
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldMoveItem(fromPosition: Int, toPosition: Int): Boolean {
|
||||
if (adapter.selectedItemCount > 1)
|
||||
return false
|
||||
if (adapter.isSelected(fromPosition))
|
||||
toggleSelection(fromPosition)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onActionStateChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
val position = viewHolder?.adapterPosition ?: return
|
||||
if (actionState == 2)
|
||||
onItemLongClick(position)
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a manga.
|
||||
*
|
||||
|
@ -26,6 +26,7 @@ import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.jakewharton.rxbinding.support.v4.view.pageSelections
|
||||
import com.jakewharton.rxbinding.support.v7.widget.queryTextChanges
|
||||
import com.jakewharton.rxbinding.view.visible
|
||||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import com.jakewharton.rxrelay.PublishRelay
|
||||
import eu.kanade.tachiyomi.R
|
||||
@ -120,6 +121,11 @@ class LibraryController(
|
||||
*/
|
||||
val selectAllRelay: PublishRelay<Int> = PublishRelay.create()
|
||||
|
||||
/**
|
||||
* Relay to notify the library's viewpager to reotagnize all
|
||||
*/
|
||||
val reorganizeRelay: PublishRelay<Pair<Int, Int>> = PublishRelay.create()
|
||||
|
||||
/**
|
||||
* Number of manga per row in grid mode.
|
||||
*/
|
||||
@ -328,6 +334,7 @@ class LibraryController(
|
||||
* Called when the sorting mode is changed.
|
||||
*/
|
||||
private fun onSortChanged() {
|
||||
activity?.invalidateOptionsMenu()
|
||||
presenter.requestSortUpdate()
|
||||
}
|
||||
|
||||
@ -364,6 +371,9 @@ class LibraryController(
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.library, menu)
|
||||
|
||||
val reorganizeItem = menu.findItem(R.id.action_reorganize)
|
||||
reorganizeItem.isVisible = preferences.librarySortingMode().getOrDefault() == LibrarySort.DRAG_AND_DROP
|
||||
|
||||
val searchItem = menu.findItem(R.id.action_search)
|
||||
val searchView = searchItem.actionView as SearchView
|
||||
|
||||
@ -417,12 +427,22 @@ class LibraryController(
|
||||
R.id.action_source_migration -> {
|
||||
router.pushController(MigrationController().withFadeTransaction())
|
||||
}
|
||||
R.id.action_alpha_asc -> reOrder(1)
|
||||
R.id.action_alpha_dsc -> reOrder(2)
|
||||
R.id.action_update_asc -> reOrder(3)
|
||||
R.id.action_update_dsc -> reOrder(4)
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun reOrder(type: Int) {
|
||||
adapter?.categories?.getOrNull(library_pager.currentItem)?.id?.let {
|
||||
reorganizeRelay.call(it to type)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invalidates the action mode, forcing it to refresh its content.
|
||||
*/
|
||||
|
@ -20,7 +20,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
*/
|
||||
class LibraryGridHolder(
|
||||
private val view: View,
|
||||
private val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
|
@ -15,7 +15,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
|
||||
abstract class LibraryHolder(
|
||||
view: View,
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
) : BaseFlexibleViewHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
@ -26,4 +26,15 @@ abstract class LibraryHolder(
|
||||
*/
|
||||
abstract fun onSetValues(item: LibraryItem)
|
||||
|
||||
|
||||
/**
|
||||
* Called when an item is released.
|
||||
*
|
||||
* @param position The position of the released item.
|
||||
*/
|
||||
override fun onItemReleased(position: Int) {
|
||||
super.onItemReleased(position)
|
||||
(adapter as? LibraryCategoryAdapter)?.onItemReleaseListener?.onItemReleased(position)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -51,6 +51,13 @@ class LibraryItem(val manga: LibraryManga, private val libraryAsList: Preference
|
||||
holder.onSetValues(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this item is draggable.
|
||||
*/
|
||||
override fun isDraggable(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters a manga depending on a query.
|
||||
*
|
||||
|
@ -21,7 +21,7 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
||||
|
||||
class LibraryListHolder(
|
||||
private val view: View,
|
||||
private val adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>
|
||||
) : LibraryHolder(view, adapter) {
|
||||
|
||||
/**
|
||||
|
@ -131,7 +131,10 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
|
||||
private val source = Item.MultiSort(R.string.manga_info_source_label, this)
|
||||
|
||||
override val items = listOf(alphabetically, lastRead, lastUpdated, unread, total, source)
|
||||
private val dragAndDrop = Item.MultiSort(R.string.action_sort_drag_and_drop, this)
|
||||
|
||||
override val items = listOf(alphabetically, lastRead, lastUpdated, unread, total, source,
|
||||
dragAndDrop)
|
||||
|
||||
override val header = Item.Header(R.string.action_sort)
|
||||
|
||||
@ -148,6 +151,7 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
unread.state = if (sorting == LibrarySort.UNREAD) order else SORT_NONE
|
||||
total.state = if (sorting == LibrarySort.TOTAL) order else SORT_NONE
|
||||
source.state = if (sorting == LibrarySort.SOURCE) order else SORT_NONE
|
||||
dragAndDrop.state = if (sorting == LibrarySort.DRAG_AND_DROP) order else SORT_NONE
|
||||
}
|
||||
|
||||
override fun onItemClicked(item: Item) {
|
||||
@ -155,6 +159,9 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
val prevState = item.state
|
||||
|
||||
item.group.items.forEach { (it as Item.MultiStateGroup).state = SORT_NONE }
|
||||
if (item == dragAndDrop)
|
||||
item.state = SORT_ASC
|
||||
else
|
||||
item.state = when (prevState) {
|
||||
SORT_NONE -> SORT_ASC
|
||||
SORT_ASC -> SORT_DESC
|
||||
@ -169,9 +176,10 @@ class LibraryNavigationView @JvmOverloads constructor(context: Context, attrs: A
|
||||
unread -> LibrarySort.UNREAD
|
||||
total -> LibrarySort.TOTAL
|
||||
source -> LibrarySort.SOURCE
|
||||
dragAndDrop -> LibrarySort.DRAG_AND_DROP
|
||||
else -> throw Exception("Unknown sorting")
|
||||
})
|
||||
preferences.librarySortingAscending().set(if (item.state == SORT_ASC) true else false)
|
||||
preferences.librarySortingAscending().set(item.state == SORT_ASC)
|
||||
|
||||
item.group.items.forEach { adapter.notifyItemChanged(it) }
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.ui.migration.MigrationFlags
|
||||
import eu.kanade.tachiyomi.util.combineLatest
|
||||
import eu.kanade.tachiyomi.util.isNullOrUnsubscribed
|
||||
import eu.kanade.tachiyomi.util.removeArticles
|
||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_EXCLUDE
|
||||
import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup.Companion.STATE_IGNORE
|
||||
@ -210,6 +211,9 @@ class LibraryPresenter(
|
||||
val mangaCompare = source1Name.compareTo(source2Name)
|
||||
if (mangaCompare == 0) sortAlphabetical(i1, i2) else mangaCompare
|
||||
}
|
||||
LibrarySort.DRAG_AND_DROP -> {
|
||||
0
|
||||
}
|
||||
else -> throw Exception("Unknown sorting mode")
|
||||
}
|
||||
}
|
||||
@ -228,10 +232,6 @@ class LibraryPresenter(
|
||||
else i1.manga.title.compareTo(i2.manga.title, true)
|
||||
}
|
||||
|
||||
private fun String.removeArticles(): String {
|
||||
return this.replace(Regex("^(an|a|the) ", RegexOption.IGNORE_CASE), "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the categories and all its manga from the database.
|
||||
*
|
||||
|
@ -8,4 +8,5 @@ object LibrarySort {
|
||||
const val UNREAD = 3
|
||||
const val TOTAL = 4
|
||||
const val SOURCE = 5
|
||||
const val DRAG_AND_DROP = 6
|
||||
}
|
@ -14,6 +14,10 @@ fun String.chop(count: Int, replacement: String = "..."): String {
|
||||
|
||||
}
|
||||
|
||||
fun String.removeArticles(): String {
|
||||
return this.replace(Regex("^(an|a|the) ", RegexOption.IGNORE_CASE), "")
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the given string to have at most [count] characters using [replacement] near the center.
|
||||
* If [replacement] is longer than [count] an exception will be thrown when `length > count`.
|
||||
|
@ -32,4 +32,24 @@
|
||||
android:title="@string/label_migration"
|
||||
app:showAsAction="never"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_reorganize"
|
||||
android:title="@string/label_reorganize_by"
|
||||
app:showAsAction="never">
|
||||
<menu>
|
||||
<item
|
||||
android:id="@+id/action_alpha_asc"
|
||||
android:title="@string/action_sort_alpha"/>
|
||||
<item
|
||||
android:id="@+id/action_alpha_dsc"
|
||||
android:title="@string/label_alpha_reverse"/>
|
||||
<item
|
||||
android:id="@+id/action_update_asc"
|
||||
android:title="@string/action_sort_last_updated"/>
|
||||
<item
|
||||
android:id="@+id/action_update_dsc"
|
||||
android:title="@string/action_sort_first_updated"/>
|
||||
</menu>
|
||||
</item>
|
||||
|
||||
</menu>
|
||||
|
@ -22,6 +22,8 @@
|
||||
<string name="label_selected">Selected: %1$d</string>
|
||||
<string name="label_backup">Backup</string>
|
||||
<string name="label_migration">Source migration</string>
|
||||
<string name="label_reorganize_by">Re-order</string>
|
||||
<string name="label_alpha_reverse">Alphabetically (descending)</string>
|
||||
<string name="label_hide_title">Hide title</string>
|
||||
<string name="label_show_title">Show title</string>
|
||||
<string name="label_extensions">Extensions</string>
|
||||
@ -43,6 +45,8 @@
|
||||
<string name="action_sort_total">Total chapters</string>
|
||||
<string name="action_sort_last_read">Last read</string>
|
||||
<string name="action_sort_last_updated">Last updated</string>
|
||||
<string name="action_sort_first_updated">First updated</string>
|
||||
<string name="action_sort_drag_and_drop">Drag & Drop</string>
|
||||
<string name="action_search">Search</string>
|
||||
<string name="action_skip_manga">Skip manga</string>
|
||||
<string name="action_global_search">Global search</string>
|
||||
|
Loading…
Reference in New Issue
Block a user