diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt index 73ff8d1f6c..b830329682 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Category.kt @@ -28,6 +28,8 @@ interface Category : Serializable { var isDynamic: Boolean + var sourceId: Long? + fun isAscending(): Boolean { return ((mangaSort?.minus('a') ?: 0) % 2) != 1 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt index 6b27e0eee8..2e08573745 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/CategoryImpl.kt @@ -20,6 +20,8 @@ class CategoryImpl : Category { override var isDynamic: Boolean = false + override var sourceId: Long? = null + override fun equals(other: Any?): Boolean { if (this === other) return true if (other == null || javaClass != other.javaClass) return false diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 66150122e0..3ede549c4f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -273,6 +273,8 @@ class PreferencesHelper(val context: Context) { fun hideHopper() = flowPrefs.getBoolean("hide_hopper", false) + fun groupLibraryBy() = flowPrefs.getInt("group_library_by", 0) + // Tutorial preferences fun shownFilterTutorial() = flowPrefs.getBoolean("shown_filter_tutorial", false) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt index d0e3ed7d77..44db0ddefa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryCategoryAdapter.kt @@ -126,15 +126,13 @@ class LibraryCategoryAdapter(val controller: LibraryController) : val text = if (item.manga.isBlank()) return item.header?.category?.name.orEmpty() else when (getSort(position)) { LibrarySort.DRAG_AND_DROP -> { - if (!preferences.hideCategories().getOrDefault()) { + if (item.header.category.isDynamic) { + val category = db.getCategoriesForManga(item.manga).executeAsBlocking().firstOrNull()?.name + category ?: recyclerView.context.getString(R.string.default_value) + } else { val title = item.manga.title - if (preferences.removeArticles().getOrDefault()) title.removeArticles() - .chop(15) + if (preferences.removeArticles().getOrDefault()) title.removeArticles().chop(15) else title.take(10) - } else { - val category = db.getCategoriesForManga(item.manga).executeAsBlocking() - .firstOrNull()?.name - category ?: recyclerView.context.getString(R.string.default_value) } } LibrarySort.LAST_READ -> { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index db1916216d..3cf32f9dc7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -51,6 +51,11 @@ import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.ui.base.controller.BaseController import eu.kanade.tachiyomi.ui.category.ManageCategoryDialog +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_DEFAULT +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_SOURCE +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_STATUS +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_TAG +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_TRACK_STATUS import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet import eu.kanade.tachiyomi.ui.main.BottomSheetController import eu.kanade.tachiyomi.ui.main.MainActivity @@ -379,7 +384,7 @@ class LibraryController( swipe_refresh.isRefreshing = false if (!LibraryUpdateService.isRunning()) { when { - !presenter.showAllCategories -> { + !presenter.showAllCategories || presenter.groupType != BY_DEFAULT -> { presenter.categories.find { it.id == presenter.currentCategory }?.let { updateLibrary(it) } @@ -425,6 +430,31 @@ class LibraryController( FilterBottomSheet.ACTION_HIDE_FILTER_TIP -> showFilterTip() FilterBottomSheet.ACTION_DISPLAY -> DisplayBottomSheet(this).show() FilterBottomSheet.ACTION_EXPAND_COLLAPSE_ALL -> presenter.toggleAllCategoryVisibility() + FilterBottomSheet.ACTION_GROUP_BY -> { + val groupItems = mutableListOf(BY_DEFAULT, BY_TAG, BY_SOURCE, BY_STATUS) + if (presenter.isLoggedIntoTracking) { + groupItems.add(BY_TRACK_STATUS) + } + val items = groupItems.map { id -> + MaterialMenuSheet.MenuSheetItem( + id, + LibraryGroup.groupTypeDrawableRes(id), + LibraryGroup.groupTypeStringRes(id) + ) + } + MaterialMenuSheet( + activity!!, + items, + activity!!.getString(R.string.group_library_by), + presenter.groupType + ) { _, item -> + preferences.groupLibraryBy().set(item) + presenter.groupType = item + recycler?.scrollToPosition(0) + presenter.getLibrary() + true + }.show() + } } } @@ -662,7 +692,8 @@ class LibraryController( category_hopper_frame.visibleIf(!singleCategory && !preferences.hideHopper().get()) filter_bottom_sheet.updateButtons( showHideCategories = presenter.allCategories.size > 1, - showExpand = !singleCategory && presenter.showAllCategories + showExpand = !singleCategory && presenter.showAllCategories, + groupType = presenter.groupType ) adapter.isLongPressDragEnabled = canDrag() category_recycler.setCategories(presenter.categories) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGroup.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGroup.kt new file mode 100644 index 0000000000..fc18d88232 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryGroup.kt @@ -0,0 +1,32 @@ +package eu.kanade.tachiyomi.ui.library + +import eu.kanade.tachiyomi.R + +object LibraryGroup { + + const val BY_DEFAULT = 0 + const val BY_TAG = 1 + const val BY_SOURCE = 2 + const val BY_STATUS = 3 + const val BY_TRACK_STATUS = 4 + + fun groupTypeStringRes(type: Int): Int { + return when (type) { + BY_STATUS -> R.string.status + BY_TAG -> R.string.tag + BY_TRACK_STATUS -> R.string.tracking + BY_SOURCE -> R.string.sources + else -> R.string.categories + } + } + + fun groupTypeDrawableRes(type: Int): Int { + return when (type) { + BY_STATUS -> R.drawable.ic_progress_clock_24dp + BY_TAG -> R.drawable.ic_style_24dp + BY_TRACK_STATUS -> R.drawable.ic_sync_black_24dp + BY_SOURCE -> R.drawable.ic_browse_24dp + else -> R.drawable.ic_label_outline_white_24dp + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt index 2f5055f3e5..5859b44f95 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryHeaderItem.kt @@ -19,6 +19,8 @@ import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.source.SourceManager +import eu.kanade.tachiyomi.source.icon import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.system.getResourceColor @@ -30,13 +32,16 @@ import eu.kanade.tachiyomi.util.view.visibleIf import kotlinx.android.synthetic.main.library_category_header_item.* import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy class LibraryHeaderItem( private val categoryF: (Int) -> Category, - private val catId: Int + val catId: Int ) : AbstractHeaderItem() { + private val sourceManager by injectLazy() + override fun getLayoutRes(): Int { return R.layout.library_category_header_item } @@ -139,6 +144,13 @@ class LibraryHeaderItem( if (category.isAlone) sectionText.text = "" else sectionText.text = category.name + if (category.sourceId != null) { + val icon = item.sourceManager.get(category.sourceId!!)?.icon() + icon?.setBounds(0, 0, 32.dpToPx, 32.dpToPx) + sectionText.setCompoundDrawablesRelative(icon, null, null, null) + } else { + sectionText.setCompoundDrawablesRelative(null, null, null, null) + } val isAscending = category.isAscending() val sortingMode = category.sortingMode() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt index cf56ad33d2..55f5168349 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryPresenter.kt @@ -1,5 +1,6 @@ package eu.kanade.tachiyomi.ui.library +import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Category @@ -15,6 +16,10 @@ import eu.kanade.tachiyomi.source.LocalSource import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_DEFAULT +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_SOURCE +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_TAG +import eu.kanade.tachiyomi.ui.library.LibraryGroup.BY_TRACK_STATUS import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_EXCLUDE import eu.kanade.tachiyomi.ui.library.filter.FilterBottomSheet.Companion.STATE_IGNORE @@ -29,7 +34,6 @@ import kotlinx.coroutines.withContext import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import java.util.ArrayList -import java.util.Collections import java.util.Comparator /** @@ -50,6 +54,11 @@ class LibraryPresenter( private val loggedServices by lazy { Injekt.get().services.filter { it.isLogged } } + var groupType = preferences.groupLibraryBy().get() + + val isLoggedIntoTracking + get() = loggedServices.isNotEmpty() + /** Current categories of the library. */ var categories: List = emptyList() private set @@ -422,63 +431,77 @@ class LibraryPresenter( val categories = db.getCategories().executeAsBlocking().toMutableList() val showCategories = !preferences.hideCategories().getOrDefault() var libraryManga = db.getLibraryMangas().executeAsBlocking() - val showAll = showAllCategories - if (!showCategories) libraryManga = libraryManga.distinctBy { it.id } - val categoryAll = Category.createAll( - context, - preferences.librarySortingMode().getOrDefault(), - preferences.librarySortingAscending().getOrDefault() - ) - val catItemAll = LibraryHeaderItem({ categoryAll }, -1) - val categorySet = mutableSetOf() - val headerItems = (categories.mapNotNull { category -> - val id = category.id - if (id == null) null - else id to LibraryHeaderItem({ getCategory(id) }, id) - } + (-1 to catItemAll) + (0 to LibraryHeaderItem({ getCategory(0) }, 0))).toMap() - val items = libraryManga.mapNotNull { - val headerItem = (if (!showCategories) catItemAll - else headerItems[it.category]) ?: return@mapNotNull null - categorySet.add(it.category) - LibraryItem(it, headerItem) - }.toMutableList() - - val categoriesHidden = preferences.collapsedCategories().getOrDefault().mapNotNull { - it.toIntOrNull() - }.toMutableSet() + if (groupType <= BY_DEFAULT || !showCategories) { + libraryManga = libraryManga.distinctBy { it.id } + } - if (categorySet.contains(0)) categories.add(0, createDefaultCategory()) - if (showCategories) { - categories.forEach { category -> - val catId = category.id ?: return@forEach - if (catId > 0 && !categorySet.contains(catId) && - (catId !in categoriesHidden || !showAll)) { - val headerItem = headerItems[catId] - if (headerItem != null) items.add( - LibraryItem(LibraryManga.createBlank(catId), headerItem) - ) - } else if (catId in categoriesHidden && showAll && categories.size > 1) { - val mangaToRemove = items.filter { it.manga.category == catId } - val mergedTitle = mangaToRemove.joinToString("-") { - it.manga.title + "-" + it.manga.author + val items = if (groupType <= BY_DEFAULT || !showCategories) { + val categoryAll = Category.createAll( + context, + preferences.librarySortingMode().getOrDefault(), + preferences.librarySortingAscending().getOrDefault() + ) + val catItemAll = LibraryHeaderItem({ categoryAll }, -1) + val categorySet = mutableSetOf() + val headerItems = (categories.mapNotNull { category -> + val id = category.id + if (id == null) null + else id to LibraryHeaderItem({ getCategory(id) }, id) + } + (-1 to catItemAll) + (0 to LibraryHeaderItem({ getCategory(0) }, 0))).toMap() + + val items = libraryManga.mapNotNull { + val headerItem = (if (!showCategories) catItemAll + else headerItems[it.category]) ?: return@mapNotNull null + categorySet.add(it.category) + LibraryItem(it, headerItem) + }.toMutableList() + + val categoriesHidden = preferences.collapsedCategories().getOrDefault().mapNotNull { + it.toIntOrNull() + }.toMutableSet() + + if (categorySet.contains(0)) categories.add(0, createDefaultCategory()) + if (showCategories) { + categories.forEach { category -> + val catId = category.id ?: return@forEach + if (catId > 0 && !categorySet.contains(catId) && (catId !in categoriesHidden || + !showCategories)) { + val headerItem = headerItems[catId] + if (headerItem != null) items.add( + LibraryItem(LibraryManga.createBlank(catId), headerItem) + ) + } else if (catId in categoriesHidden && showCategories && categories.size > 1) { + val mangaToRemove = items.filter { it.manga.category == catId } + val mergedTitle = mangaToRemove.joinToString("-") { + it.manga.title + "-" + it.manga.author + } + sectionedLibraryItems[catId] = mangaToRemove + items.removeAll(mangaToRemove) + val headerItem = headerItems[catId] + if (headerItem != null) items.add( + LibraryItem(LibraryManga.createHide(catId, mergedTitle), headerItem) + ) } - sectionedLibraryItems[catId] = mangaToRemove - items.removeAll(mangaToRemove) - val headerItem = headerItems[catId] - if (headerItem != null) items.add( - LibraryItem(LibraryManga.createHide(catId, mergedTitle), headerItem) - ) } } - } - categories.forEach { - it.isHidden = it.id in categoriesHidden && showAll + categories.forEach { + it.isHidden = it.id in categoriesHidden && showCategories + } + this.categories = if (!showCategories) { + arrayListOf(categoryAll) + } else { + categories + } + + items + } else { + val (items, categories) = getCustomMangaItems(libraryManga) + this.categories = categories + items } this.allCategories = categories - this.categories = if (!showCategories) arrayListOf(categoryAll) - else categories hashCategories = HashMap(this.categories.mapNotNull { it.id!! to it @@ -487,6 +510,86 @@ class LibraryPresenter( return items } + private fun getCustomMangaItems(libraryManga: List): Pair, + List> { + val tagItems: MutableMap = mutableMapOf() + + // internal function to make headers + fun makeOrGetHeader(name: String): LibraryHeaderItem { + return if (tagItems.containsKey(name)) { + tagItems[name]!! + } else { + val headerItem = LibraryHeaderItem({ getCategory(it) }, tagItems.count()) + tagItems[name] = headerItem + headerItem + } + } + + val items = libraryManga.mapNotNull { manga -> + when (groupType) { + BY_TAG -> { + val tags = if (manga.genre.isNullOrBlank()) { + listOf("Unknown") + } else { + manga.genre?.split(",")?.mapNotNull { + val tag = it.trim() + if (tag.isBlank()) null else tag + } ?: listOf("Unknown") + } + tags.map { + LibraryItem(manga, makeOrGetHeader(it)) + } + } + BY_TRACK_STATUS -> { + val status: String = { + val tracks = db.getTracks(manga).executeAsBlocking() + val track = tracks.find { track -> + loggedServices.any { it.id == track?.sync_id } + } + if (track != null) { + loggedServices.find { it.id == track.sync_id }?.getStatus(track.status) + ?: context.getString(R.string.unknown) + } else { + context.getString(R.string.not_tracked) + } + }() + listOf(LibraryItem(manga, makeOrGetHeader(status))) + } + BY_SOURCE -> { + val source = sourceManager.getOrStub(manga.source) + listOf(LibraryItem(manga, makeOrGetHeader("${source.name}◘•◘${source.id}"))) + } + else -> listOf(LibraryItem(manga, makeOrGetHeader(mapStatus(manga.status)))) + } + }.flatten() + + val headers = tagItems.map { item -> + Category.createCustom( + item.key, + preferences.librarySortingMode().getOrDefault(), + preferences.librarySortingAscending().getOrDefault() + ).apply { + id = item.value.catId + if (name.contains("◘•◘")) { + val split = name.split("◘•◘") + name = split.first() + sourceId = split.last().toLongOrNull() + } + } + }.sortedBy { it.name } + headers.forEachIndexed { index, category -> category.order = index } + return items to headers + } + + private fun mapStatus(status: Int): String { + return context.getString(when (status) { + SManga.LICENSED -> R.string.licensed + SManga.ONGOING -> R.string.ongoing + SManga.COMPLETED -> R.string.completed + else -> R.string.unknown + }) + } + /** Create a default category with the sort set */ private fun createDefaultCategory(): Category { val default = Category.createDefault(context) @@ -632,6 +735,9 @@ class LibraryPresenter( val sort = category.sortingMode() ?: LibrarySort.ALPHA preferences.librarySortingMode().set(sort) preferences.librarySortingAscending().set(category.isAscending()) + categories.forEach { + it.mangaSort = category.mangaSort + } } else if (catId >= 0) { if (category.id == 0) preferences.defaultMangaOrder().set(category.mangaSort.toString()) else Injekt.get().insertCategory(category).executeAsBlocking() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt index ab611c1dae..4769916f5e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/filter/FilterBottomSheet.kt @@ -20,6 +20,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.data.preference.getOrDefault import eu.kanade.tachiyomi.data.track.TrackManager +import eu.kanade.tachiyomi.ui.library.LibraryGroup import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.view.collapse import eu.kanade.tachiyomi.util.view.gone @@ -126,6 +127,9 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri expand_categories.setOnClickListener { onGroupClicked(ACTION_EXPAND_COLLAPSE_ALL) } + group_by.setOnClickListener { + onGroupClicked(ACTION_GROUP_BY) + } view_options.setOnClickListener { onGroupClicked(ACTION_DISPLAY) } @@ -413,8 +417,14 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri }?.set(index + 1) onGroupClicked(ACTION_FILTER) } + val hasFilters = hasActiveFilters() + if (hasFilters && clearButton.parent == null) { + filter_layout.addView(clearButton, 0) + } else if (!hasFilters && clearButton.parent != null) { + filter_layout.removeView(clearButton) + } if (tracked?.isActivated == true && trackers != null && trackers?.parent == null) { - filter_layout.addView(trackers, filterItems.indexOf(tracked!!) + 1) + filter_layout.addView(trackers, filterItems.indexOf(tracked!!) + 2) filterItems.add(filterItems.indexOf(tracked!!) + 1, trackers!!) } else if (tracked?.isActivated == false && trackers?.parent != null) { filter_layout.removeView(trackers) @@ -422,20 +432,15 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri FILTER_TRACKER = "" filterItems.remove(trackers!!) } - val hasFilters = hasActiveFilters() - if (hasFilters && clearButton.parent == null) { - filter_layout.addView(clearButton, 0) - } else if (!hasFilters && clearButton.parent != null) { - filter_layout.removeView(clearButton) - } } - fun updateButtons(showHideCategories: Boolean, showExpand: Boolean) { + fun updateButtons(showHideCategories: Boolean, showExpand: Boolean, groupType: Int) { hide_categories.visibleIf(showHideCategories) - expand_categories.visibleIf(showExpand) + expand_categories.visibleIf(showExpand && groupType == 0) first_layout.visibleIf( hide_categories.isVisible() || expand_categories.isVisible() || !second_layout.isVisible() ) + group_by.setIconResource(LibraryGroup.groupTypeDrawableRes(groupType)) } private fun clearFilters() { @@ -479,6 +484,7 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri const val ACTION_HIDE_FILTER_TIP = 2 const val ACTION_DISPLAY = 3 const val ACTION_EXPAND_COLLAPSE_ALL = 4 + const val ACTION_GROUP_BY = 5 const val STATE_IGNORE = 0 const val STATE_INCLUDE = 1 diff --git a/app/src/main/res/drawable/ic_progress_clock_24dp.xml b/app/src/main/res/drawable/ic_progress_clock_24dp.xml new file mode 100644 index 0000000000..a0f4b9cc0d --- /dev/null +++ b/app/src/main/res/drawable/ic_progress_clock_24dp.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_style_24dp.xml b/app/src/main/res/drawable/ic_style_24dp.xml new file mode 100644 index 0000000000..8ffd623dc9 --- /dev/null +++ b/app/src/main/res/drawable/ic_style_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/filter_bottom_sheet.xml b/app/src/main/res/layout/filter_bottom_sheet.xml index 595b98ea81..75312191b8 100644 --- a/app/src/main/res/layout/filter_bottom_sheet.xml +++ b/app/src/main/res/layout/filter_bottom_sheet.xml @@ -89,6 +89,18 @@ app:iconTint="?android:attr/textColorPrimary" /> + + Read progress Series type + Group library by… Sort by @@ -713,6 +714,7 @@ Sort & Filter Start Stop + Tag Top Undo Unknown error