From 20bab59df38007848947bdb9f5d21572b8086f63 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 3 May 2020 01:00:45 -0400 Subject: [PATCH] Jump to category using the backdrop on the top of the library Along with only showing a single category at a time --- .../data/preference/PreferencesHelper.kt | 2 + .../ui/library/LibraryCategoryAdapter.kt | 67 ++++++--- .../tachiyomi/ui/library/LibraryController.kt | 136 ++++++++++++++++-- .../tachiyomi/ui/library/LibraryHeaderItem.kt | 7 +- .../tachiyomi/ui/library/LibraryPresenter.kt | 81 ++++++++--- .../ui/library/category/CategoryItem.kt | 36 +++++ .../library/category/CategoryRecyclerView.kt | 70 +++++++++ .../res/drawable/ic_arrow_drop_down_24dp.xml | 5 + .../res/drawable/ic_arrow_drop_up_24dp.xml | 5 + .../main/res/drawable/list_item_selector.xml | 2 +- .../main/res/layout/catergory_text_view.xml | 20 +++ .../layout/library_category_header_item.xml | 10 +- .../main/res/layout/library_grid_recycler.xml | 3 +- .../res/layout/library_list_controller.xml | 29 ++++ app/src/main/res/layout/main_activity.xml | 10 ++ app/src/main/res/layout/manga_grid_item.xml | 1 - .../res/layout/manga_recycler_autofit.xml | 1 - 17 files changed, 421 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryItem.kt create mode 100644 app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryRecyclerView.kt create mode 100644 app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml create mode 100644 app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml create mode 100644 app/src/main/res/layout/catergory_text_view.xml 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 93a7732475..8d66ef2c24 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 @@ -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) 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 f964ce354c..0e406c0a0b 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 @@ -21,8 +21,8 @@ import kotlin.math.max * * @param view the fragment containing this adapter. */ -class LibraryCategoryAdapter(val libraryListener: LibraryListener) : - FlexibleAdapter>(null, libraryListener, true) { +class LibraryCategoryAdapter(val controller: LibraryController) : + FlexibleAdapter>(null, controller, true) { init { setDisplayHeadersAtStartUp(true) @@ -32,6 +32,8 @@ class LibraryCategoryAdapter(val libraryListener: LibraryListener) : */ private var mangas: List = 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" } } 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 cf69f45b3e..6aed3cff02 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 @@ -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 { + topMargin = marginTop + insets.systemWindowInsetTop + } + category_layout?.updateLayoutParams { + topMargin = marginTop + insets.systemWindowInsetTop + } + recycler_cover?.updateLayoutParams { + topMargin = marginTop + insets.systemWindowInsetTop + } fast_scroller?.updateLayoutParams { 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, 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) { 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 ac5f600c64..1d778ee4fd 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 @@ -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) } } } 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 57702fc095..a65054249d 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 @@ -60,7 +60,12 @@ class LibraryPresenter( /** List of all manga to update the */ var libraryItems: List = emptyList() + private var sectionedLibraryItems: Map> = emptyMap() + var currentCategory = -1 + private set private var allLibraryItems: List = emptyList() + val showAllCategories + get() = preferences.showAllCategories().get() private var totalChapters: Map? = 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, 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) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryItem.kt new file mode 100644 index 0000000000..40b0c4401f --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryItem.kt @@ -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() { + + /** 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(view) { + val categoryTitle: TextView = view.findViewById(R.id.category_text) + + override fun bindView(item: CategoryItem, payloads: List) { + categoryTitle.text = item.category.name + } + + override fun unbindView(item: CategoryItem) { + categoryTitle.text = null + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryRecyclerView.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryRecyclerView.kt new file mode 100644 index 0000000000..1275af58c2 --- /dev/null +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/category/CategoryRecyclerView.kt @@ -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 + var onCategoryClicked: (Int) -> Unit = { _ -> } + private val itemAdapter = ItemAdapter() + var selectedCategory: Int = 0 + + init { + fastAdapter = FastAdapter.with(itemAdapter) + layoutManager = manager + adapter = fastAdapter + } + + fun setCategories(items: List) { + itemAdapter.set(items.map(::CategoryItem)) + fastAdapter.onBindViewHolderListener = (object : OnBindViewHolderListenerImpl() { + override fun onBindViewHolder( + viewHolder: ViewHolder, + position: Int, + payloads: List + ) { + 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(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) + } +} diff --git a/app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml b/app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml new file mode 100644 index 0000000000..65e1e42281 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_down_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml b/app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml new file mode 100644 index 0000000000..1d266cecc8 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_drop_up_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/list_item_selector.xml b/app/src/main/res/drawable/list_item_selector.xml index a35650003e..c20696a339 100644 --- a/app/src/main/res/drawable/list_item_selector.xml +++ b/app/src/main/res/drawable/list_item_selector.xml @@ -15,7 +15,7 @@ - + diff --git a/app/src/main/res/layout/catergory_text_view.xml b/app/src/main/res/layout/catergory_text_view.xml new file mode 100644 index 0000000000..8e8ac44bdf --- /dev/null +++ b/app/src/main/res/layout/catergory_text_view.xml @@ -0,0 +1,20 @@ + + + + + diff --git a/app/src/main/res/layout/library_category_header_item.xml b/app/src/main/res/layout/library_category_header_item.xml index a4a2a32935..f543647b49 100644 --- a/app/src/main/res/layout/library_category_header_item.xml +++ b/app/src/main/res/layout/library_category_header_item.xml @@ -8,6 +8,14 @@ android:background="@drawable/list_item_selector" android:gravity="center_vertical"> + + + android:background="@drawable/bottom_sheet_rounded_background" + tools:listitem="@layout/manga_grid_item"/> diff --git a/app/src/main/res/layout/library_list_controller.xml b/app/src/main/res/layout/library_list_controller.xml index e3090a1674..a597c0743c 100644 --- a/app/src/main/res/layout/library_list_controller.xml +++ b/app/src/main/res/layout/library_list_controller.xml @@ -22,8 +22,37 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + + + + + + + + + + + diff --git a/app/src/main/res/layout/manga_grid_item.xml b/app/src/main/res/layout/manga_grid_item.xml index 4b34c4e518..d1c74c90f5 100644 --- a/app/src/main/res/layout/manga_grid_item.xml +++ b/app/src/main/res/layout/manga_grid_item.xml @@ -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"> diff --git a/app/src/main/res/layout/manga_recycler_autofit.xml b/app/src/main/res/layout/manga_recycler_autofit.xml index 7db468e7cb..9a5afbc3c2 100644 --- a/app/src/main/res/layout/manga_recycler_autofit.xml +++ b/app/src/main/res/layout/manga_recycler_autofit.xml @@ -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" /> \ No newline at end of file