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 a7d0d956a9..68e2e61124 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 @@ -291,11 +291,11 @@ class LibraryController( val tv = TypedValue() activity!!.theme.resolveAttribute(R.attr.actionBarTintColor, tv, true) swipe_refresh.setStyle() - scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh) { insets -> + scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { insets -> fast_scroller.updateLayoutParams { topMargin = insets.systemWindowInsetTop } - } + }) swipe_refresh.setOnRefreshListener { swipe_refresh.isRefreshing = false 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 d8d44690b4..7841a0500f 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 @@ -134,5 +134,6 @@ class MangaDetailsAdapter( fun copyToClipboard(content: String, label: Int) fun zoomImageFromThumb(thumbView: View) fun showTrackingSheet() + fun updateScroll() } } 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 7141d0ef86..eef9aca26e 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 @@ -35,7 +35,6 @@ import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils -import androidx.core.math.MathUtils import androidx.palette.graphics.Palette import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager @@ -98,8 +97,8 @@ import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.pxToDp import eu.kanade.tachiyomi.util.system.toast -import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets import eu.kanade.tachiyomi.util.view.getText +import eu.kanade.tachiyomi.util.view.scrollViewWith import eu.kanade.tachiyomi.util.view.setOnQueryTextChangeListener import eu.kanade.tachiyomi.util.view.setStyle import eu.kanade.tachiyomi.util.view.snack @@ -114,7 +113,6 @@ import uy.kohesive.injekt.api.get import java.io.File import java.io.IOException import java.util.Locale -import kotlin.math.abs import kotlin.math.max class MangaDetailsController : BaseController, @@ -261,98 +259,54 @@ class MangaDetailsController : BaseController, val appbarHeight = array.getDimensionPixelSize(0, 0) array.recycle() val offset = 10.dpToPx - var statusBarHeight = -1 swipe_refresh.setStyle() swipe_refresh.setDistanceToTriggerSync(70.dpToPx) activity!!.appbar.elevation = 0f - recycler.doOnApplyWindowInsets { v, insets, _ -> - v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) + scrollViewWith(recycler, padBottom = true, customPadding = true, afterInsets = { insets -> + recycler.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) tabletRecycler?.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) headerHeight = appbarHeight + insets.systemWindowInsetTop - statusBarHeight = insets.systemWindowInsetTop swipe_refresh.setProgressViewOffset(false, (-40).dpToPx, headerHeight + offset) - if (isTablet) v.updatePaddingRelative(top = headerHeight + 1.dpToPx) + // 1dp extra to line up chapter header and manga header + if (isTablet) recycler.updatePaddingRelative(top = headerHeight + 1.dpToPx) getHeader()?.setTopHeight(headerHeight) fast_scroll_layout.updateLayoutParams { topMargin = headerHeight bottomMargin = insets.systemWindowInsetBottom } - v.updatePaddingRelative(bottom = insets.systemWindowInsetBottom) - } + }, liftOnScroll = { + colorToolbar(it) + }) recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) - val atTop = !recycler.canScrollVertically(-1) if (!isTablet) { + val atTop = !recycler.canScrollVertically(-1) val tY = getHeader()?.backdrop?.translationY ?: 0f getHeader()?.backdrop?.translationY = max(0f, tY + dy * 0.25f) - if (router?.backstack?.lastOrNull() - ?.controller() == this@MangaDetailsController && statusBarHeight > -1 && activity != null && activity!!.appbar.height > 0 - ) { - activity!!.appbar.y -= dy - activity!!.appbar.y = MathUtils.clamp( - activity!!.appbar.y, -activity!!.appbar.height.toFloat(), 0f - ) - } - val appBarY = activity?.appbar?.y ?: 0f - if ((!atTop && !toolbarIsColored && (appBarY < (-headerHeight + 1) || (dy < 0 && appBarY == 0f))) || (atTop && toolbarIsColored)) { - colorToolbar(!atTop) - } - } else { - if ((!atTop && !toolbarIsColored) || (atTop && toolbarIsColored)) { - colorToolbar(!atTop) - } - } - if (atTop) { - getHeader()?.backdrop?.translationY = 0f - activity!!.appbar.y = 0f - } - if (!isTablet) { + if (atTop) getHeader()?.backdrop?.translationY = 0f val fPosition = (recycler.layoutManager as LinearLayoutManager).findFirstCompletelyVisibleItemPosition() + val scrollFunc: (Boolean) -> Unit = { show -> + showScroll = show + scrollAnim?.cancel() + scrollAnim = fast_scroller.animate().setDuration(100).translationX( + if (show) 0f else 25f.dpToPx) + scrollAnim?.start() + } if (fPosition > 0 && !showScroll) { - showScroll = true - scrollAnim?.cancel() - scrollAnim = fast_scroller.animate().setDuration(100).translationX(0f) - scrollAnim?.start() + scrollFunc(true) } else if (fPosition <= 0 && showScroll) { - showScroll = false - scrollAnim?.cancel() - scrollAnim = - fast_scroller.animate().setDuration(100).translationX(25f.dpToPx) - scrollAnim?.start() + scrollFunc(false) } } } override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) val atTop = !recycler.canScrollVertically(-1) - if (newState == RecyclerView.SCROLL_STATE_IDLE && !isTablet) { - if (router?.backstack?.lastOrNull() - ?.controller() == this@MangaDetailsController && statusBarHeight > -1 && activity != null && - activity!!.appbar.height > 0 - ) { - val halfWay = abs((-activity!!.appbar.height.toFloat()) / 2) - val shortAnimationDuration = resources?.getInteger( - android.R.integer.config_shortAnimTime - ) ?: 0 - val closerToTop = abs(activity!!.appbar.y) - halfWay > 0 - activity!!.appbar.animate().y( - if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat()) - else 0f - ).setDuration(shortAnimationDuration.toLong()).start() - if (!closerToTop && !atTop && !toolbarIsColored) - colorToolbar(true) - } - } - if (atTop && toolbarIsColored) colorToolbar(false) - if (atTop) { - getHeader()?.backdrop?.translationY = 0f - activity!!.appbar.y = 0f - } + if (atTop) getHeader()?.backdrop?.translationY = 0f } }) } @@ -1004,6 +958,15 @@ class MangaDetailsController : BaseController, presenter.startDownloadingNow(chapter) } + // In case the recycler is at the bottom and collapsing the header makes it unscrollable + override fun updateScroll() { + if (!recycler.canScrollVertically(-1)) { + getHeader()?.backdrop?.translationY = 0f + activity?.appbar?.y = 0f + colorToolbar(isColor = false, animate = false) + } + } + private fun downloadChapters(chapters: List) { val view = view ?: return presenter.downloadChapters(chapters) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt index 1d00a0ab3e..4d5a4cd980 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaHeaderHolder.kt @@ -69,6 +69,9 @@ class MangaHeaderHolder( adapter.delegate.favoriteManga(true) true } + title.setOnClickListener { + title.maxLines = Integer.MAX_VALUE + } title.setOnLongClickListener { adapter.delegate.copyToClipboard(title.text.toString(), R.string.title) true @@ -102,6 +105,9 @@ class MangaHeaderHolder( manga_genres_tags.gone() less_button.gone() more_button_group.visible() + adapter.recyclerView.post { + adapter.delegate.updateScroll() + } } fun bindChapters() { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt index 58ab1c0231..8a281edafa 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsController.kt @@ -117,9 +117,9 @@ class RecentsController(bundle: Bundle? = null) : BaseController(bundle), val appBarHeight = array.getDimensionPixelSize(0, 0) array.recycle() swipe_refresh.setStyle() - scrollViewWith(recycler, skipFirstSnap = true, swipeRefreshLayout = swipe_refresh) { + scrollViewWith(recycler, swipeRefreshLayout = swipe_refresh, afterInsets = { headerHeight = it.systemWindowInsetTop + appBarHeight - } + }) presenter.onCreate() if (presenter.recentItems.isNotEmpty()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt index 969f052f70..7c07ee171e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/SourceController.kt @@ -121,9 +121,9 @@ class SourceController : NucleusController(), val array = view.context.obtainStyledAttributes(attrsArray) val appBarHeight = array.getDimensionPixelSize(0, 0) array.recycle() - scrollViewWith(recycler) { + scrollViewWith(recycler, afterInsets = { headerHeight = it.systemWindowInsetTop + appBarHeight - } + }) requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301) ext_bottom_sheet.onCreate(this) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt index 9d81286eaf..be885e2ad2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/browse/BrowseSourceController.kt @@ -184,11 +184,11 @@ open class BrowseSourceController(bundle: Bundle) : recycler.setHasFixedSize(true) recycler.adapter = adapter - scrollViewWith(recycler, true) { insets -> + scrollViewWith(recycler, true, afterInsets = { insets -> fab.updateLayoutParams { bottomMargin = insets.systemWindowInsetBottom + 16.dpToPx } - } + }) recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt index 9bc3abbe22..210ecc4080 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/view/ControllerExtensions.kt @@ -9,7 +9,6 @@ import android.view.inputmethod.InputMethodManager import androidx.appcompat.widget.SearchView import androidx.core.content.ContextCompat import androidx.core.math.MathUtils -import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import com.bluelinelabs.conductor.Controller @@ -53,9 +52,9 @@ fun Controller.scrollViewWith( recycler: RecyclerView, padBottom: Boolean = false, customPadding: Boolean = false, - skipFirstSnap: Boolean = false, swipeRefreshLayout: SwipeRefreshLayout? = null, - afterInsets: ((WindowInsets) -> Unit)? = null + afterInsets: ((WindowInsets) -> Unit)? = null, + liftOnScroll: ((Boolean) -> Unit)? = null ) { var statusBarHeight = -1 activity?.appbar?.y = 0f @@ -87,14 +86,18 @@ fun Controller.scrollViewWith( var elevate = false val elevateFunc: (Boolean) -> Unit = { el -> elevate = el - elevationAnim?.cancel() - elevationAnim = ValueAnimator.ofFloat( - activity!!.appbar.elevation, if (el) 15f else 0f - ) - elevationAnim?.addUpdateListener { valueAnimator -> - activity!!.appbar.elevation = valueAnimator.animatedValue as Float + if (liftOnScroll != null) { + liftOnScroll.invoke(el) + } else { + elevationAnim?.cancel() + elevationAnim = ValueAnimator.ofFloat( + activity!!.appbar.elevation, if (el) 15f else 0f + ) + elevationAnim?.addUpdateListener { valueAnimator -> + activity!!.appbar.elevation = valueAnimator.animatedValue as Float + } + elevationAnim?.start() } - elevationAnim?.start() } addLifecycleListener(object : Controller.LifecycleListener() { override fun onChangeStart( @@ -144,10 +147,7 @@ fun Controller.scrollViewWith( R.integer.config_shortAnimTime ) ?: 0 val closerToTop = abs(activity!!.appbar.y) - halfWay > 0 - val atTop = (!customPadding && - (recycler.layoutManager as LinearLayoutManager) - .findFirstVisibleItemPosition() < 2 && !skipFirstSnap) || - !recycler.canScrollVertically(-1) + val atTop = !recycler.canScrollVertically(-1) activity!!.appbar.animate().y( if (closerToTop && !atTop) (-activity!!.appbar.height.toFloat()) else 0f