From 4dae06803ce0127187d702d7fc07bc1386ed4f82 Mon Sep 17 00:00:00 2001 From: Jay Date: Sun, 10 May 2020 03:03:36 -0400 Subject: [PATCH] Using material fast scroll on manga details This concludes the use of reddit's fastscroll Also updated the fast scroll's scroll listener so that the scroll handle respects its margins (thus making it reach the bottom of the manga details --- app/build.gradle.kts | 1 - .../ui/library/MaterialFastScroll.kt | 22 +++++++ .../tachiyomi/ui/manga/MangaDetailsAdapter.kt | 53 +++++---------- .../ui/manga/MangaDetailsController.kt | 66 +------------------ .../ui/manga/MangaDetailsPresenter.kt | 14 ++-- .../util/view/FastScrollerExtensions.kt | 37 ----------- .../res/layout/manga_details_controller.xml | 44 ++----------- 7 files changed, 51 insertions(+), 186 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/tachiyomi/util/view/FastScrollerExtensions.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 3b4003d72d..42278fcf8c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -206,7 +206,6 @@ dependencies { implementation("me.zhanghai.android.systemuihelper:library:1.0.0") implementation("com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.1.0") implementation("com.github.mthli:Slice:v1.2") - implementation("com.reddit:indicator-fast-scroll:1.2.1") implementation("com.github.kizitonwose:AndroidTagGroup:1.6.0") implementation("com.github.chrisbanes:PhotoView:2.3.0") diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/MaterialFastScroll.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/MaterialFastScroll.kt index fee9c6209f..a1a3cb345c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/MaterialFastScroll.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/MaterialFastScroll.kt @@ -3,9 +3,12 @@ package eu.kanade.tachiyomi.ui.library import android.content.Context import android.util.AttributeSet import android.view.MotionEvent +import androidx.recyclerview.widget.RecyclerView import eu.davidea.fastscroller.FastScroller import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.util.system.dpToPxEnd +import eu.kanade.tachiyomi.util.view.marginTop +import kotlin.math.abs class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : FastScroller(context, attrs) { @@ -16,6 +19,7 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr ) autoHideEnabled = true ignoreTouchesOutsideHandle = true + updateScrollListener() } override fun onTouchEvent(event: MotionEvent): Boolean { @@ -30,4 +34,22 @@ class MaterialFastScroll @JvmOverloads constructor(context: Context, attrs: Attr bubble.translationX = (-45f).dpToPxEnd } } + + private fun updateScrollListener() { + onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + if (!isEnabled || bubble == null || handle.isSelected) return + val verticalScrollOffset = recyclerView.computeVerticalScrollOffset() + val verticalScrollRange = recyclerView.computeVerticalScrollRange() - marginTop + val proportion = + verticalScrollOffset.toFloat() / (verticalScrollRange - height).toFloat() + setBubbleAndHandlePosition(height * proportion) + // If scroll amount is small, don't show it + if (minimumScrollThreshold == 0 || dy == 0 || abs(dy) > minimumScrollThreshold || scrollbarAnimator.isAnimating) { + showScrollbar() + if (autoHideEnabled) hideScrollbar() + } + } + } + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt index 162b9bbbc6..b3c55d6c60 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsAdapter.kt @@ -59,54 +59,33 @@ class MangaDetailsAdapter( } } - fun getSectionText(position: Int): String? { - val chapter = getItem(position) as? ChapterItem ?: return null - if (position == itemCount - 1) return "-" - return when (presenter.scrollType) { - MangaDetailsPresenter.MULTIPLE_VOLUMES, MangaDetailsPresenter.MULTIPLE_SEASONS -> - presenter.getGroupNumber(chapter)?.toString() ?: "*" - MangaDetailsPresenter.HUNDREDS_OF_CHAPTERS -> - if (chapter.chapter_number < 0) "*" - else (chapter.chapter_number / 100).toInt().toString() - MangaDetailsPresenter.TENS_OF_CHAPTERS -> - if (chapter.chapter_number < 0) "*" - else (chapter.chapter_number / 10).toInt().toString() - else -> null - } - } - - fun getFullText(position: Int): String { + override fun onCreateBubbleText(position: Int): String { val chapter = getItem(position) as? ChapterItem ?: return recyclerView.context.getString(R.string.top) - if (position == itemCount - 1) return recyclerView.context.getString(R.string.bottom) return when (val scrollType = presenter.scrollType) { MangaDetailsPresenter.MULTIPLE_VOLUMES, MangaDetailsPresenter.MULTIPLE_SEASONS -> { val volume = presenter.getGroupNumber(chapter) - if (volume != null) recyclerView.context.getString( - if (scrollType == MangaDetailsPresenter.MULTIPLE_SEASONS) R.string.season_ - else R.string.volume_, volume) - else recyclerView.context.getString(R.string.unknown) + if (volume != null) { + recyclerView.context.getString( + if (scrollType == MangaDetailsPresenter.MULTIPLE_SEASONS) R.string.season_ + else R.string.volume_, volume + ) + } else { + recyclerView.context.getString(R.string.unknown) + } } - MangaDetailsPresenter.HUNDREDS_OF_CHAPTERS -> recyclerView.context.getString( - R.string.chapters_, get100sRange( - chapter.chapter_number - ) - ) MangaDetailsPresenter.TENS_OF_CHAPTERS -> recyclerView.context.getString( R.string.chapters_, get10sRange( chapter.chapter_number ) ) - else -> recyclerView.context.getString(R.string.unknown) - } - } - - private fun get100sRange(value: Float): String { - val number = value.toInt() - return if (number < 100) "0-99" - else { - val hundred = number / 100 - "${hundred}00-${hundred}99" + else -> if (chapter.chapter_number > 0) { + recyclerView.context.getString( + R.string.chapter_, decimalFormat.format(chapter.chapter_number) + ) + } else { + chapter.name + } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index feb45edf64..2a92a1c256 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -53,8 +53,6 @@ import com.bumptech.glide.request.transition.Transition import com.bumptech.glide.signature.ObjectKey import com.google.android.material.snackbar.BaseTransientBottomBar import com.google.android.material.snackbar.Snackbar -import com.reddit.indicatorfastscroll.FastScrollItemIndicator -import com.reddit.indicatorfastscroll.FastScrollerView import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.SelectableAdapter import eu.kanade.tachiyomi.R @@ -101,14 +99,10 @@ import eu.kanade.tachiyomi.util.system.isOnline import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.view.getText -import eu.kanade.tachiyomi.util.view.hide import eu.kanade.tachiyomi.util.view.requestPermissionsSafe import eu.kanade.tachiyomi.util.view.scrollViewWith -import eu.kanade.tachiyomi.util.view.setBackground import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener -import eu.kanade.tachiyomi.util.view.setStartTranslationX import eu.kanade.tachiyomi.util.view.setStyle -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 @@ -205,7 +199,7 @@ class MangaDetailsController : BaseController, setRecycler(view) setPaletteColor() - setFastScroller() + adapter?.fastScroller = fast_scroller2 presenter.onCreate() swipe_refresh.isRefreshing = presenter.isLoading @@ -259,17 +253,6 @@ class MangaDetailsController : BaseController, override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { val atTop = !recycler.canScrollVertically(-1) if (atTop) getHeader()?.backdrop?.translationY = 0f - when (newState) { - RecyclerView.SCROLL_STATE_DRAGGING -> { - scrollAnim?.cancel() - if (fast_scroller.translationX != 0f) { - showFastScroller() - } - } - RecyclerView.SCROLL_STATE_IDLE -> { - scrollAnim = fast_scroller.hide() - } - } } }) } @@ -281,51 +264,12 @@ class MangaDetailsController : BaseController, swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight + offset) // 1dp extra to line up chapter header and manga header getHeader()?.setTopHeight(headerHeight) - fast_scroll_layout.updateLayoutParams { + fast_scroller2.updateLayoutParams { topMargin = headerHeight bottomMargin = insets.systemWindowInsetBottom } } - private fun setFastScroller() { - fast_scroller.setStartTranslationX(true) - fast_scroller.setBackground(true) - fast_scroller.setupWithRecyclerView(recycler, { position -> - val letter = adapter?.getSectionText(position) - when { - presenter.scrollType == 0 -> null - letter != null -> FastScrollItemIndicator.Text(letter) - else -> FastScrollItemIndicator.Icon(R.drawable.ic_star_24dp) - } - }) - fast_scroller.useDefaultScroller = false - fast_scroller.itemIndicatorSelectedCallbacks += object : - FastScrollerView.ItemIndicatorSelectedCallback { - override fun onItemIndicatorSelected( - indicator: FastScrollItemIndicator, - indicatorCenterY: Int, - itemPosition: Int - ) { - scrollAnim?.cancel() - scrollAnim = fast_scroller.hide(2000) - - textAnim?.cancel() - textAnim = text_view_m.animate().alpha(0f).setDuration(250L).setStartDelay(1000) - textAnim?.start() - - text_view_m.translationY = indicatorCenterY.toFloat() - text_view_m.height / 2 - text_view_m.alpha = 1f - text_view_m.text = adapter?.getFullText(itemPosition) - val appbar = activity?.appbar - appbar?.y = 0f - (recycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset( - itemPosition, headerHeight - ) - colorToolbar(itemPosition > 0, false) - } - } - } - /** Set the toolbar to fully transparent or colored and translucent */ fun colorToolbar(isColor: Boolean, animate: Boolean = true) { if (isColor == toolbarIsColored) return @@ -594,12 +538,6 @@ class MangaDetailsController : BaseController, activity?.invalidateOptionsMenu() } - private fun showFastScroller(animate: Boolean = true) { - if (presenter.scrollType != 0) { - fast_scroller.show(animate) - } - } - private fun addMangaHeader() { if (adapter?.scrollableHeaders?.isEmpty() == true) { adapter?.removeAllScrollableHeaders() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 74a08cb67f..fbe601e08a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -251,7 +251,6 @@ class MangaDetailsPresenter( scrollType = when { hasMultipleVolumes(chapters) -> MULTIPLE_VOLUMES hasMultipleSeasons(chapters) -> MULTIPLE_SEASONS - hasHundredsOfChapters(chapters) -> HUNDREDS_OF_CHAPTERS hasTensOfChapters(chapters) -> TENS_OF_CHAPTERS else -> 0 } @@ -283,7 +282,7 @@ class MangaDetailsPresenter( val volNum = getVolumeNumber(it) if (volNum != null) { volumeSet.add(volNum) - if (volumeSet.size >= 3) return true + if (volumeSet.size >= 2) return true } } return false @@ -295,18 +294,14 @@ class MangaDetailsPresenter( val volNum = getSeasonNumber(it) if (volNum != null) { volumeSet.add(volNum) - if (volumeSet.size >= 3) return true + if (volumeSet.size >= 2) return true } } return false } - private fun hasHundredsOfChapters(chapters: List): Boolean { - return chapters.size > 300 - } - private fun hasTensOfChapters(chapters: List): Boolean { - return chapters.size in 21..300 + return chapters.size > 20 } /** @@ -878,7 +873,6 @@ class MangaDetailsPresenter( companion object { const val MULTIPLE_VOLUMES = 1 const val TENS_OF_CHAPTERS = 2 - const val HUNDREDS_OF_CHAPTERS = 3 - const val MULTIPLE_SEASONS = 4 + const val MULTIPLE_SEASONS = 3 } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/FastScrollerExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/FastScrollerExtensions.kt deleted file mode 100644 index 50eb2f677d..0000000000 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/FastScrollerExtensions.kt +++ /dev/null @@ -1,37 +0,0 @@ -package eu.kanade.tachiyomi.util.view - -import android.content.res.ColorStateList -import android.graphics.Color -import android.view.ViewPropertyAnimator -import androidx.core.content.ContextCompat -import com.reddit.indicatorfastscroll.FastScrollerView -import eu.kanade.tachiyomi.R -import eu.kanade.tachiyomi.util.system.dpToPxEnd -import eu.kanade.tachiyomi.util.system.getResourceColor - -fun FastScrollerView.setBackground(hasBackground: Boolean) { - background = if (hasBackground) ContextCompat.getDrawable( - context, R.drawable.fast_scroll_background - ) else null - textColor = ColorStateList.valueOf( - if (hasBackground) Color.WHITE - else context.getResourceColor(android.R.attr.textColorPrimary) - ) - iconColor = textColor -} - -fun FastScrollerView.setStartTranslationX(shouldHide: Boolean) { - translationX = if (shouldHide) 25f.dpToPxEnd else 0f -} - -fun FastScrollerView.hide(duration: Long = 1000): ViewPropertyAnimator { - val scrollAnim = animate().setStartDelay(duration).setDuration(250) - .translationX(25f.dpToPxEnd) - scrollAnim.start() - return scrollAnim -} - -fun FastScrollerView.show(animate: Boolean = true) { - if (animate) animate().setStartDelay(0).setDuration(100).translationX(0f).start() - else translationX = 0f -} diff --git a/app/src/main/res/layout/manga_details_controller.xml b/app/src/main/res/layout/manga_details_controller.xml index fd6035f177..9ae2abb61e 100644 --- a/app/src/main/res/layout/manga_details_controller.xml +++ b/app/src/main/res/layout/manga_details_controller.xml @@ -14,55 +14,25 @@ android:background="?android:colorBackground"> - + android:layout_height="match_parent"> - + - - - - - - + android:layout_height="match_parent" + app:fastScrollerBubbleEnabled="true" />