mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-12-23 18:41:48 +01:00
Jump to category using the backdrop on the top of the library
Along with only showing a single category at a time
This commit is contained in:
parent
49b18181e7
commit
20bab59df3
@ -263,6 +263,8 @@ class PreferencesHelper(val context: Context) {
|
||||
|
||||
fun deleteRemovedChapters() = flowPrefs.getInt(Keys.deleteRemovedChapters, 0)
|
||||
|
||||
fun showAllCategories() = flowPrefs.getBoolean("show_all_categories", true)
|
||||
|
||||
// Tutorial preferences
|
||||
fun shownFilterTutorial() = flowPrefs.getBoolean("shown_filter_tutorial", false)
|
||||
|
||||
|
@ -21,8 +21,8 @@ import kotlin.math.max
|
||||
*
|
||||
* @param view the fragment containing this adapter.
|
||||
*/
|
||||
class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
FlexibleAdapter<IFlexible<*>>(null, libraryListener, true) {
|
||||
class LibraryCategoryAdapter(val controller: LibraryController) :
|
||||
FlexibleAdapter<IFlexible<*>>(null, controller, true) {
|
||||
|
||||
init {
|
||||
setDisplayHeadersAtStartUp(true)
|
||||
@ -32,6 +32,8 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
*/
|
||||
private var mangas: List<LibraryItem> = emptyList()
|
||||
|
||||
val libraryListener: LibraryListener = controller
|
||||
|
||||
/**
|
||||
* Sets a list of manga in the adapter.
|
||||
*
|
||||
@ -97,13 +99,18 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
val preferences: PreferencesHelper by injectLazy()
|
||||
val db: DatabaseHelper by injectLazy()
|
||||
if (position == itemCount - 1) return "-"
|
||||
val sorting = if (preferences.hideCategories().getOrDefault())
|
||||
preferences.hideCategories().getOrDefault()
|
||||
else (headerItems.firstOrNull() as? LibraryHeaderItem)?.category?.sortingMode()
|
||||
?: LibrarySort.DRAG_AND_DROP
|
||||
val sorting = if (!preferences.showAllCategories().get()) {
|
||||
controller.presenter.getCurrentCategory()?.sortingMode() ?: LibrarySort.DRAG_AND_DROP
|
||||
} else if (preferences.hideCategories().getOrDefault()) {
|
||||
preferences.librarySortingMode().getOrDefault()
|
||||
} else {
|
||||
(headerItems.firstOrNull() as? LibraryHeaderItem)?.category?.sortingMode()
|
||||
?: LibrarySort.DRAG_AND_DROP
|
||||
}
|
||||
return when (val item: IFlexible<*>? = getItem(position)) {
|
||||
is LibraryHeaderItem ->
|
||||
if (preferences.hideCategories().getOrDefault() || item.category.id == 0) null
|
||||
if (preferences.hideCategories().getOrDefault() || item.category.id == 0 ||
|
||||
!preferences.showAllCategories().get()) null
|
||||
else getFirstChar(item.category.name) +
|
||||
"\u200B".repeat(max(0, item.category.order))
|
||||
is LibraryItem -> {
|
||||
@ -123,11 +130,11 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
}
|
||||
LibrarySort.TOTAL -> {
|
||||
val unread = item.chapterCount
|
||||
(unread / 100).toString()
|
||||
getShortRange(unread)
|
||||
}
|
||||
LibrarySort.UNREAD -> {
|
||||
val unread = item.manga.unread
|
||||
if (unread > 0) (unread / 100).toString()
|
||||
if (unread > 0) getShortRange(unread)
|
||||
else "R"
|
||||
}
|
||||
LibrarySort.LATEST_CHAPTER -> {
|
||||
@ -168,7 +175,9 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
else recyclerView.context.getString(R.string.top)
|
||||
is LibraryItem -> {
|
||||
if (iFlexible.manga.isBlank()) ""
|
||||
else when (preferences.librarySortingMode().getOrDefault()) {
|
||||
else when (if (!preferences.showAllCategories().get()) {
|
||||
controller.presenter.getCurrentCategory()?.sortingMode() ?: LibrarySort.DRAG_AND_DROP
|
||||
} else preferences.librarySortingMode().getOrDefault()) {
|
||||
LibrarySort.DRAG_AND_DROP -> {
|
||||
if (!preferences.hideCategories().getOrDefault()) {
|
||||
val title = iFlexible.manga.title
|
||||
@ -225,18 +234,36 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) :
|
||||
}
|
||||
}
|
||||
|
||||
private fun getShortRange(value: Int): String {
|
||||
return when (value) {
|
||||
1 -> "1"
|
||||
2 -> "2"
|
||||
3 -> "3"
|
||||
4 -> "4"
|
||||
5 -> "5"
|
||||
in 6..10 -> "6"
|
||||
in 11..50 -> "10"
|
||||
in 51..100 -> "50"
|
||||
in 101..500 -> "1+"
|
||||
in 499..899 -> "4+"
|
||||
in 901..Int.MAX_VALUE -> "9+"
|
||||
else -> "0"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRange(value: Int): String {
|
||||
return when (value) {
|
||||
in 1..99 -> "< 100"
|
||||
in 100..199 -> "100-199"
|
||||
in 200..299 -> "200-299"
|
||||
in 300..399 -> "300-399"
|
||||
in 400..499 -> "400-499"
|
||||
in 500..599 -> "500-599"
|
||||
in 600..699 -> "600-699"
|
||||
in 700..799 -> "700-799"
|
||||
in 800..899 -> "800-899"
|
||||
in 900..Int.MAX_VALUE -> "900+"
|
||||
1 -> "1"
|
||||
2 -> "2"
|
||||
3 -> "3"
|
||||
4 -> "4"
|
||||
5 -> "5"
|
||||
in 6..10 -> "6-10"
|
||||
in 11..50 -> "11-50"
|
||||
in 51..100 -> "51-100"
|
||||
in 101..500 -> "100-500"
|
||||
in 499..899 -> "499-900"
|
||||
in 901..Int.MAX_VALUE -> "900+"
|
||||
else -> "None"
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
@ -16,6 +17,7 @@ import android.view.inputmethod.InputMethodManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ActionMode
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
@ -56,10 +58,11 @@ import eu.kanade.tachiyomi.util.system.dpToPxEnd
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.launchUI
|
||||
import eu.kanade.tachiyomi.util.view.applyWindowInsetsForRootController
|
||||
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
|
||||
import eu.kanade.tachiyomi.util.view.getItemView
|
||||
import eu.kanade.tachiyomi.util.view.gone
|
||||
import eu.kanade.tachiyomi.util.view.hide
|
||||
import eu.kanade.tachiyomi.util.view.scrollViewWith
|
||||
import eu.kanade.tachiyomi.util.view.marginTop
|
||||
import eu.kanade.tachiyomi.util.view.setBackground
|
||||
import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener
|
||||
import eu.kanade.tachiyomi.util.view.setStartTranslationX
|
||||
@ -68,6 +71,7 @@ import eu.kanade.tachiyomi.util.view.show
|
||||
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.view.visibleIf
|
||||
import eu.kanade.tachiyomi.util.view.withFadeTransaction
|
||||
import kotlinx.android.synthetic.main.filter_bottom_sheet.*
|
||||
import kotlinx.android.synthetic.main.library_grid_recycler.*
|
||||
@ -142,7 +146,8 @@ class LibraryController(
|
||||
private var scrollAnim: ViewPropertyAnimator? = null
|
||||
private var alwaysShowScroller: Boolean = preferences.alwaysShowSeeker().getOrDefault()
|
||||
private var filterTooltip: ViewTooltip? = null
|
||||
|
||||
private var elevationAnim: ValueAnimator? = null
|
||||
private var elevate = false
|
||||
override fun getTitle(): String? {
|
||||
return view?.context?.getString(R.string.library)
|
||||
}
|
||||
@ -150,6 +155,8 @@ class LibraryController(
|
||||
private var scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val notAtTop = recycler.canScrollVertically(-1)
|
||||
if (notAtTop != elevate) elevateFunc(notAtTop)
|
||||
val order = getCategoryOrder()
|
||||
if (filter_bottom_sheet.sheetBehavior?.state != BottomSheetBehavior.STATE_HIDDEN) {
|
||||
scrollDistance += abs(dy)
|
||||
@ -161,6 +168,7 @@ class LibraryController(
|
||||
if (order != null && order != activeCategory && lastItem == null) {
|
||||
preferences.lastUsedCategory().set(order)
|
||||
activeCategory = order
|
||||
setActiveCategory()
|
||||
if (presenter.categories.size > 1 && dy != 0) {
|
||||
val headerItem = getHeader() ?: return
|
||||
val view = fast_scroller?.getChildAt(0) ?: return
|
||||
@ -226,6 +234,12 @@ class LibraryController(
|
||||
fast_scroller.setStartTranslationX(!alwaysShowScroller)
|
||||
fast_scroller.setBackground(!alwaysShowScroller)
|
||||
|
||||
show_all.isChecked = preferences.showAllCategories().get()
|
||||
show_all.setOnCheckedChangeListener { _, isChecked ->
|
||||
preferences.showAllCategories().set(isChecked)
|
||||
presenter.getLibrary()
|
||||
}
|
||||
|
||||
adapter = LibraryCategoryAdapter(this)
|
||||
adapter.expandItemsAtStartUp()
|
||||
adapter.isRecursiveCollapse = true
|
||||
@ -243,7 +257,7 @@ class LibraryController(
|
||||
recycler.adapter = adapter
|
||||
fast_scroller.setupWithRecyclerView(recycler, { position ->
|
||||
val letter = adapter.getSectionText(position)
|
||||
if (!singleCategory &&
|
||||
if (!singleCategory && presenter.showAllCategories &&
|
||||
!adapter.isHeader(adapter.getItem(position)) &&
|
||||
position != adapter.itemCount - 1) null
|
||||
else if (letter != null) FastScrollItemIndicator.Text(letter)
|
||||
@ -287,8 +301,7 @@ class LibraryController(
|
||||
appbar?.y = 0f
|
||||
recycler.suppressLayout(true)
|
||||
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
itemPosition,
|
||||
if (singleCategory) 0 else (if (itemPosition == 0) 0 else (-40).dpToPx)
|
||||
itemPosition, 0
|
||||
)
|
||||
recycler.suppressLayout(false)
|
||||
}
|
||||
@ -298,16 +311,43 @@ class LibraryController(
|
||||
val tv = TypedValue()
|
||||
activity!!.theme.resolveAttribute(R.attr.actionBarTintColor, tv, true)
|
||||
swipe_refresh.setStyle()
|
||||
scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { insets ->
|
||||
|
||||
recycler_cover.setOnClickListener {
|
||||
showCategories(false)
|
||||
}
|
||||
category_recycler.onCategoryClicked = {
|
||||
scrollToHeader(it)
|
||||
showCategories(false)
|
||||
}
|
||||
swipe_refresh.setDistanceToTriggerSync(150.dpToPx)
|
||||
val marginTop = category_layout.marginTop
|
||||
recycler.doOnApplyWindowInsets { recyclerView, insets, _ ->
|
||||
recyclerView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = marginTop + insets.systemWindowInsetTop
|
||||
}
|
||||
category_layout?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = marginTop + insets.systemWindowInsetTop
|
||||
}
|
||||
recycler_cover?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = marginTop + insets.systemWindowInsetTop
|
||||
}
|
||||
fast_scroller?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||
topMargin = insets.systemWindowInsetTop
|
||||
}
|
||||
})
|
||||
swipe_refresh?.setProgressViewOffset(
|
||||
true, (marginTop + insets.systemWindowInsetTop) + (-60).dpToPx, marginTop + insets.systemWindowInsetTop + 10.dpToPx
|
||||
)
|
||||
}
|
||||
|
||||
swipe_refresh.setOnRefreshListener {
|
||||
swipe_refresh.isRefreshing = false
|
||||
if (!LibraryUpdateService.isRunning()) {
|
||||
when {
|
||||
!presenter.showAllCategories -> {
|
||||
presenter.categories.find { it.id == presenter.currentCategory }?.let {
|
||||
updateLibrary(it)
|
||||
}
|
||||
}
|
||||
presenter.allCategories.size <= 1 -> updateLibrary()
|
||||
preferences.updateOnRefresh().getOrDefault() == -1 -> {
|
||||
MaterialDialog(activity!!).title(R.string.what_should_update)
|
||||
@ -357,7 +397,7 @@ class LibraryController(
|
||||
|
||||
presenter.onRestore()
|
||||
if (presenter.libraryItems.isNotEmpty()) {
|
||||
onNextLibraryUpdate(presenter.libraryItems, true)
|
||||
presenter.restoreLibrary()
|
||||
} else {
|
||||
recycler_layout.alpha = 0f
|
||||
presenter.getLibrary()
|
||||
@ -453,7 +493,13 @@ class LibraryController(
|
||||
presenter.getLibrary()
|
||||
DownloadService.callListeners()
|
||||
LibraryUpdateService.setListener(this)
|
||||
} else closeTip()
|
||||
recycler_cover.isClickable = false
|
||||
recycler_cover.isFocusable = false
|
||||
activity?.dropdown?.visibleIf(!singleCategory)
|
||||
} else {
|
||||
closeTip()
|
||||
activity?.dropdown?.gone()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
@ -482,7 +528,7 @@ class LibraryController(
|
||||
}
|
||||
|
||||
fun onNextLibraryUpdate(mangaMap: List<LibraryItem>, freshStart: Boolean = false) {
|
||||
if (view == null) return
|
||||
val view = view ?: return
|
||||
destroyActionModeIfNeeded()
|
||||
if (mangaMap.isNotEmpty()) {
|
||||
empty_view?.hide()
|
||||
@ -494,8 +540,12 @@ class LibraryController(
|
||||
)
|
||||
}
|
||||
adapter.setItems(mangaMap)
|
||||
if (recycler.itemAnimator == null)
|
||||
recycler.post {
|
||||
recycler.itemAnimator = DefaultItemAnimator()
|
||||
}
|
||||
singleCategory = presenter.categories.size <= 1
|
||||
|
||||
activity?.dropdown?.visibleIf(!singleCategory)
|
||||
progress.gone()
|
||||
if (!freshStart) {
|
||||
justStarted = false
|
||||
@ -506,15 +556,69 @@ class LibraryController(
|
||||
scrollToHeader(activeCategory)
|
||||
if (!alwaysShowScroller) {
|
||||
fast_scroller?.show(false)
|
||||
view?.post {
|
||||
view.post {
|
||||
scrollAnim = fast_scroller?.hide(2000)
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.isLongPressDragEnabled = canDrag()
|
||||
category_recycler.setCategories(presenter.categories)
|
||||
setActiveCategory()
|
||||
activity?.toolbar?.setOnClickListener {
|
||||
val recycler = recycler ?: return@setOnClickListener
|
||||
if (singleCategory) {
|
||||
recycler.scrollToPosition(0)
|
||||
} else {
|
||||
showCategories(recycler.translationY == 0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCategories(show: Boolean) {
|
||||
recycler_cover.isClickable = show
|
||||
recycler_cover.isFocusable = show
|
||||
val translateY = if (show) category_layout.height.toFloat() + 12f.dpToPx else 0f
|
||||
recycler.animate().translationY(translateY).start()
|
||||
recycler_cover.animate().translationY(translateY).start()
|
||||
recycler_cover.animate().alpha(if (show) 0.75f else 0f).start()
|
||||
if (show) {
|
||||
elevateFunc(false)
|
||||
activity?.dropdown?.setImageResource(R.drawable.ic_arrow_drop_up_24dp)
|
||||
} else {
|
||||
val notAtTop = recycler.canScrollVertically(-1)
|
||||
if (notAtTop != elevate) elevateFunc(notAtTop)
|
||||
activity?.dropdown?.setImageResource(R.drawable.ic_arrow_drop_down_24dp)
|
||||
}
|
||||
}
|
||||
|
||||
private fun elevateFunc(el: Boolean) {
|
||||
elevate = el
|
||||
elevationAnim?.cancel()
|
||||
elevationAnim = ValueAnimator.ofFloat(
|
||||
activity?.appbar?.elevation ?: 0f, if (el) 15f else 0f
|
||||
)
|
||||
elevationAnim?.addUpdateListener { valueAnimator ->
|
||||
activity?.appbar?.elevation = valueAnimator.animatedValue as Float
|
||||
}
|
||||
elevationAnim?.start()
|
||||
}
|
||||
|
||||
fun setActiveCategory() {
|
||||
val currentCategory = presenter.categories.indexOfFirst {
|
||||
if (presenter.showAllCategories) it.order == activeCategory else presenter.currentCategory == it.id
|
||||
}
|
||||
category_recycler.setCategories(currentCategory)
|
||||
}
|
||||
|
||||
private fun scrollToHeader(pos: Int) {
|
||||
if (!presenter.showAllCategories) {
|
||||
recycler.itemAnimator = null
|
||||
presenter.switchSection(pos)
|
||||
activeCategory = pos
|
||||
setActiveCategory()
|
||||
recycler.scrollToPosition(0)
|
||||
return
|
||||
}
|
||||
val headerPosition = adapter.indexOf(pos)
|
||||
if (headerPosition > -1) {
|
||||
val appbar = activity?.appbar
|
||||
@ -523,7 +627,7 @@ class LibraryController(
|
||||
view?.rootWindowInsets?.systemWindowInsetTop ?: 0
|
||||
) ?: 0f).roundToInt() + 30.dpToPx
|
||||
(recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
|
||||
headerPosition, (if (headerPosition == 0) 0 else (-40).dpToPx) + appbarOffset
|
||||
headerPosition, (if (headerPosition == 0) 0 else (-32).dpToPx) + appbarOffset
|
||||
)
|
||||
recycler.suppressLayout(false)
|
||||
}
|
||||
@ -815,6 +919,10 @@ class LibraryController(
|
||||
}
|
||||
|
||||
override fun toggleCategoryVisibility(position: Int) {
|
||||
if (!presenter.showAllCategories) {
|
||||
showCategories(true)
|
||||
return
|
||||
}
|
||||
val catId = (adapter.getItem(position) as? LibraryHeaderItem)?.category?.id ?: return
|
||||
presenter.toggleCategoryVisibility(catId)
|
||||
}
|
||||
@ -842,6 +950,7 @@ class LibraryController(
|
||||
return items.all { adapter.isSelected(it) }
|
||||
}
|
||||
|
||||
//region sheet methods
|
||||
override fun showSheet() {
|
||||
closeTip()
|
||||
when {
|
||||
@ -875,6 +984,7 @@ class LibraryController(
|
||||
}
|
||||
return false
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region Toolbar options methods
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
|
@ -36,6 +36,7 @@ import eu.kanade.tachiyomi.util.view.invisible
|
||||
import eu.kanade.tachiyomi.util.view.updateLayoutParams
|
||||
import eu.kanade.tachiyomi.util.view.visInvisIf
|
||||
import eu.kanade.tachiyomi.util.view.visible
|
||||
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
|
||||
@ -184,16 +185,16 @@ class LibraryHeaderItem(
|
||||
updateButton.gone()
|
||||
}
|
||||
LibraryUpdateService.categoryInQueue(category.id) -> {
|
||||
expandImage.visible()
|
||||
expandImage.visibleIf(adapter.headerItems.size > 1)
|
||||
checkboxImage.gone()
|
||||
catProgress.visible()
|
||||
updateButton.invisible()
|
||||
}
|
||||
else -> {
|
||||
expandImage.visible()
|
||||
expandImage.visibleIf(adapter.headerItems.size > 1)
|
||||
catProgress.gone()
|
||||
checkboxImage.gone()
|
||||
updateButton.visInvisIf(category.id ?: 0 > -1)
|
||||
updateButton.visInvisIf(category.id ?: 0 > -1 && adapter.headerItems.size > 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,12 @@ class LibraryPresenter(
|
||||
|
||||
/** List of all manga to update the */
|
||||
var libraryItems: List<LibraryItem> = emptyList()
|
||||
private var sectionedLibraryItems: Map<Int, List<LibraryItem>> = emptyMap()
|
||||
var currentCategory = -1
|
||||
private set
|
||||
private var allLibraryItems: List<LibraryItem> = emptyList()
|
||||
val showAllCategories
|
||||
get() = preferences.showAllCategories().get()
|
||||
|
||||
private var totalChapters: Map<Long, Int>? = null
|
||||
|
||||
@ -92,16 +97,56 @@ class LibraryPresenter(
|
||||
mangaMap = applyFilters(mangaMap)
|
||||
mangaMap = applySort(mangaMap)
|
||||
val freshStart = libraryItems.isEmpty()
|
||||
libraryItems = mangaMap
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(libraryItems, freshStart)
|
||||
}
|
||||
sectionLibrary(mangaMap, freshStart)
|
||||
withContext(Dispatchers.IO) {
|
||||
setTotalChapters()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentCategory() = categories.find { it.id == currentCategory }
|
||||
|
||||
fun switchSection(order: Int) {
|
||||
preferences.lastUsedCategory().set(order)
|
||||
val category = categories.find { it.order == order }?.id ?: return
|
||||
currentCategory = category
|
||||
view.onNextLibraryUpdate(sectionedLibraryItems[currentCategory] ?: emptyList())
|
||||
}
|
||||
|
||||
fun restoreLibrary() {
|
||||
val items = libraryItems
|
||||
val show = showAllCategories || preferences.hideCategories().getOrDefault()
|
||||
if (!show) {
|
||||
sectionedLibraryItems = items.groupBy { it.manga.category }
|
||||
if (currentCategory == -1) currentCategory = categories.find {
|
||||
it.order == preferences.lastUsedCategory().getOrDefault()
|
||||
}?.id ?: 0
|
||||
}
|
||||
view.onNextLibraryUpdate(
|
||||
if (!show) sectionedLibraryItems[currentCategory]
|
||||
?: sectionedLibraryItems[categories.first().id] ?: emptyList()
|
||||
else libraryItems, true
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sectionLibrary(items: List<LibraryItem>, freshStart: Boolean = false) {
|
||||
libraryItems = items
|
||||
val show = showAllCategories || preferences.hideCategories().getOrDefault()
|
||||
if (!show) {
|
||||
sectionedLibraryItems = items.groupBy { it.manga.category }
|
||||
if (currentCategory == -1) currentCategory = categories.find {
|
||||
it.order == preferences.lastUsedCategory().getOrDefault()
|
||||
}?.id ?: 0
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(
|
||||
if (!show) sectionedLibraryItems[currentCategory]
|
||||
?: sectionedLibraryItems[categories.first().id] ?: emptyList()
|
||||
else libraryItems, freshStart
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies library filters to the given list of manga.
|
||||
*
|
||||
@ -402,6 +447,7 @@ class LibraryPresenter(
|
||||
val showCategories = !preferences.hideCategories().getOrDefault()
|
||||
var libraryManga = db.getLibraryMangas().executeAsBlocking()
|
||||
val seekPref = preferences.alwaysShowSeeker()
|
||||
val showAll = showAllCategories
|
||||
if (!showCategories) libraryManga = libraryManga.distinctBy { it.id }
|
||||
val categoryAll = Category.createAll(
|
||||
context,
|
||||
@ -430,12 +476,13 @@ class LibraryPresenter(
|
||||
if (showCategories) {
|
||||
categories.forEach { category ->
|
||||
val catId = category.id ?: return@forEach
|
||||
if (catId > 0 && !categorySet.contains(catId) && catId !in categoriesHidden) {
|
||||
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) {
|
||||
} else if (catId in categoriesHidden && showAll) {
|
||||
val mangaToRemove = items.filter { it.manga.category == catId }
|
||||
val mergedTitle = mangaToRemove.joinToString("-") {
|
||||
it.manga.title + "-" + it.manga.author
|
||||
@ -450,7 +497,7 @@ class LibraryPresenter(
|
||||
}
|
||||
|
||||
categories.forEach {
|
||||
it.isHidden = it.id in categoriesHidden
|
||||
it.isHidden = it.id in categoriesHidden && showAll
|
||||
}
|
||||
|
||||
this.allCategories = categories
|
||||
@ -476,10 +523,7 @@ class LibraryPresenter(
|
||||
var mangaMap = allLibraryItems
|
||||
mangaMap = applyFilters(mangaMap)
|
||||
mangaMap = applySort(mangaMap)
|
||||
libraryItems = mangaMap
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(libraryItems)
|
||||
}
|
||||
sectionLibrary(mangaMap)
|
||||
}
|
||||
}
|
||||
|
||||
@ -491,10 +535,7 @@ class LibraryPresenter(
|
||||
allLibraryItems = mangaMap
|
||||
val current = libraryItems
|
||||
setDownloadCount(current)
|
||||
libraryItems = current
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(libraryItems)
|
||||
}
|
||||
sectionLibrary(current)
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,10 +547,7 @@ class LibraryPresenter(
|
||||
allLibraryItems = mangaMap
|
||||
val current = libraryItems
|
||||
setUnreadBadge(current)
|
||||
libraryItems = current
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(libraryItems)
|
||||
}
|
||||
sectionLibrary(current)
|
||||
}
|
||||
}
|
||||
|
||||
@ -518,10 +556,7 @@ class LibraryPresenter(
|
||||
scope.launch {
|
||||
var mangaMap = libraryItems
|
||||
mangaMap = applySort(mangaMap)
|
||||
libraryItems = mangaMap
|
||||
withContext(Dispatchers.Main) {
|
||||
view.onNextLibraryUpdate(libraryItems)
|
||||
}
|
||||
sectionLibrary(mangaMap)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,36 @@
|
||||
package eu.kanade.tachiyomi.ui.library.category
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.items.AbstractItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
|
||||
class CategoryItem(val category: Category) :
|
||||
AbstractItem<CategoryItem.ViewHolder>() {
|
||||
|
||||
/** defines the type defining this item. must be unique. preferably an id */
|
||||
override val type: Int = R.id.category_text
|
||||
|
||||
/** defines the layout which will be used for this item in the list */
|
||||
override val layoutRes: Int = R.layout.catergory_text_view
|
||||
|
||||
override var identifier = category.id?.toLong() ?: -1L
|
||||
|
||||
override fun getViewHolder(v: View): ViewHolder {
|
||||
return ViewHolder(v)
|
||||
}
|
||||
|
||||
class ViewHolder(view: View) : FastAdapter.ViewHolder<CategoryItem>(view) {
|
||||
val categoryTitle: TextView = view.findViewById(R.id.category_text)
|
||||
|
||||
override fun bindView(item: CategoryItem, payloads: List<Any>) {
|
||||
categoryTitle.text = item.category.name
|
||||
}
|
||||
|
||||
override fun unbindView(item: CategoryItem) {
|
||||
categoryTitle.text = null
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package eu.kanade.tachiyomi.ui.library.category
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.mikepenz.fastadapter.FastAdapter
|
||||
import com.mikepenz.fastadapter.adapters.ItemAdapter
|
||||
import com.mikepenz.fastadapter.listeners.OnBindViewHolderListenerImpl
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.util.view.marginBottom
|
||||
import eu.kanade.tachiyomi.util.view.marginTop
|
||||
|
||||
class CategoryRecyclerView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null
|
||||
) : RecyclerView(context, attrs) {
|
||||
|
||||
val manager = LinearLayoutManager(context)
|
||||
private val fastAdapter: FastAdapter<CategoryItem>
|
||||
var onCategoryClicked: (Int) -> Unit = { _ -> }
|
||||
private val itemAdapter = ItemAdapter<CategoryItem>()
|
||||
var selectedCategory: Int = 0
|
||||
|
||||
init {
|
||||
fastAdapter = FastAdapter.with(itemAdapter)
|
||||
layoutManager = manager
|
||||
adapter = fastAdapter
|
||||
}
|
||||
|
||||
fun setCategories(items: List<Category>) {
|
||||
itemAdapter.set(items.map(::CategoryItem))
|
||||
fastAdapter.onBindViewHolderListener = (object : OnBindViewHolderListenerImpl<CategoryItem>() {
|
||||
override fun onBindViewHolder(
|
||||
viewHolder: ViewHolder,
|
||||
position: Int,
|
||||
payloads: List<Any>
|
||||
) {
|
||||
super.onBindViewHolder(viewHolder, position, payloads)
|
||||
(viewHolder as? CategoryItem.ViewHolder)?.categoryTitle?.isSelected =
|
||||
selectedCategory == position
|
||||
}
|
||||
})
|
||||
fastAdapter.onClickListener = { _, _, item, _ ->
|
||||
onCategoryClicked(item.category.order)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun setCategories(selected: Int) {
|
||||
selectedCategory = selected
|
||||
for (i in 0..manager.itemCount) {
|
||||
(findViewHolderForAdapterPosition(i) as? CategoryItem.ViewHolder)?.categoryTitle?.isSelected =
|
||||
selectedCategory == i
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMeasure(widthSpec: Int, heightSpec: Int) {
|
||||
val recyclerView = (parent.parent as ViewGroup).findViewById<RecyclerView>(R.id.recycler)
|
||||
val top = recyclerView.marginTop
|
||||
val bottom = recyclerView.marginBottom
|
||||
val parent = recyclerView.measuredHeight - top - bottom
|
||||
val heightS = if (parent > 0)
|
||||
MeasureSpec.makeMeasureSpec(parent, MeasureSpec.AT_MOST)
|
||||
else heightSpec
|
||||
super.onMeasure(widthSpec, heightS)
|
||||
}
|
||||
}
|
5
app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M7,10l5,5 5,-5z"/>
|
||||
</vector>
|
5
app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FF000000" android:pathData="M7,14l5,-5 5,5z"/>
|
||||
</vector>
|
@ -15,7 +15,7 @@
|
||||
</item>
|
||||
|
||||
<item android:id="@android:id/mask">
|
||||
<color android:color="@android:color/transparent" />
|
||||
<color android:color="?android:colorBackground" />
|
||||
</item>
|
||||
</selector>
|
||||
</item>
|
||||
|
20
app/src/main/res/layout/catergory_text_view.xml
Normal file
20
app/src/main/res/layout/catergory_text_view.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:background="@drawable/list_item_selector">
|
||||
|
||||
<com.google.android.material.textview.MaterialTextView
|
||||
android:id="@+id/category_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/list_item_selector"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingBottom="8dp"
|
||||
tools:text="@string/categories"
|
||||
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:textSize="16sp" />
|
||||
</FrameLayout>
|
@ -8,6 +8,14 @@
|
||||
android:background="@drawable/list_item_selector"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<Space
|
||||
android:id="@+id/start_space"
|
||||
android:layout_width="6dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/category_title"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/category_title"
|
||||
android:layout_height="1dp"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
@ -42,7 +50,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:barrierDirection="right"
|
||||
app:constraint_referenced_ids="collapse_arrow,checkbox"
|
||||
app:constraint_referenced_ids="start_space,collapse_arrow,checkbox"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
|
@ -7,4 +7,5 @@
|
||||
android:layout_height="match_parent"
|
||||
android:columnWidth="140dp"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/manga_grid_item" />
|
||||
android:background="@drawable/bottom_sheet_rounded_background"
|
||||
tools:listitem="@layout/manga_grid_item"/>
|
||||
|
@ -22,8 +22,37 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/category_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_marginTop="?actionBarSize"
|
||||
android:orientation="vertical"
|
||||
android:gravity="top"
|
||||
android:layout_height="wrap_content" >
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/show_all"
|
||||
android:layout_marginStart="6dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/show_all"/>
|
||||
|
||||
<eu.kanade.tachiyomi.ui.library.category.CategoryRecyclerView
|
||||
android:id="@+id/category_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/library_grid_recycler" />
|
||||
|
||||
<View
|
||||
android:id="@+id/recycler_cover"
|
||||
android:layout_marginTop="?actionBarSize"
|
||||
android:alpha="0"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="?android:colorBackground"/>
|
||||
</FrameLayout>
|
||||
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
|
@ -55,6 +55,16 @@
|
||||
android:textColor="?actionBarTintColor"
|
||||
android:textSize="20sp"
|
||||
tools:text="Title Text" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dropdown"
|
||||
android:visibility="gone"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginStart="4dp"
|
||||
android:src="@drawable/ic_arrow_drop_down_24dp"
|
||||
android:tint="?actionBarTintColor" />
|
||||
</LinearLayout>
|
||||
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
@ -4,7 +4,6 @@
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/manga_layout"
|
||||
android:layout_width="match_parent"
|
||||
tools:background="?android:attr/colorBackground"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom">
|
||||
|
||||
|
@ -9,5 +9,4 @@
|
||||
android:columnWidth="120dp"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/manga_grid_item"
|
||||
android:background="?android:colorBackground"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto" />
|
Loading…
Reference in New Issue
Block a user