Edge-to-edge manga details view (#5613)

* Prepare for edge-to-edge MangaController

* Fix derpy liftToScroll with our own implementation

* Edge-to-edge MangaController

Except when legacy blue theme is used.

* Save app bar lift state for controller backstack

* Fix expanded cover position after the view recycled

* Handle overlap changes when incognito mode disabled

* Tablet fixes

* Revert "Handle overlap changes when incognito mode disabled"

This reverts commit 1f492449

Breaks on rotation changes.

* Fix MangaController's swipe refresh position

* All controllers are now doing lift app bar on scroll by default

They are already doing that before so this pretty much just a cleanups.

* TachiyomiCoordinatorLayout: Support ViewPager for app bar lift state check

I'm willing to revert this if this minute detail solution is deemed too hacky xD

* Fix app bar not lifted when scrolled without fling

* Save app bar lift state across configuration changes

* Fix MangaController's swipe refresh position after configuration change

* TachiyomiCoordinatorLayout: Update ViewPager reference when controller is changed
This commit is contained in:
Ivan Iskandar
2021-08-19 20:12:52 +07:00
committed by GitHub
parent 914b686c8e
commit da16110e1c
20 changed files with 490 additions and 90 deletions
@@ -13,16 +13,21 @@ import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.annotation.FloatRange
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode
import androidx.core.os.bundleOf
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.children
import androidx.core.view.doOnLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import coil.imageLoader
import coil.request.ImageRequest
import com.bluelinelabs.conductor.ControllerChangeHandler
@@ -51,7 +56,7 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.source.online.HttpSource
import eu.kanade.tachiyomi.ui.base.controller.FabController
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
import eu.kanade.tachiyomi.ui.base.controller.ToolbarLiftOnScrollController
import eu.kanade.tachiyomi.ui.base.controller.getMainAppBarHeight
import eu.kanade.tachiyomi.ui.base.controller.withFadeTransaction
import eu.kanade.tachiyomi.ui.browse.migration.search.SearchController
import eu.kanade.tachiyomi.ui.browse.source.browse.BrowseSourceController
@@ -89,6 +94,7 @@ import eu.kanade.tachiyomi.util.view.snack
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.recyclerview.scrollEvents
import reactivecircus.flowbinding.recyclerview.scrollStateChanges
import reactivecircus.flowbinding.swiperefreshlayout.refreshes
import timber.log.Timber
import uy.kohesive.injekt.Injekt
@@ -99,7 +105,6 @@ import kotlin.math.min
class MangaController :
NucleusController<MangaControllerBinding, MangaPresenter>,
ToolbarLiftOnScrollController,
FabController,
ActionMode.Callback,
FlexibleAdapter.OnItemClickListener,
@@ -254,6 +259,37 @@ class MangaController :
updateToolbarTitleAlpha()
}
}
it.scrollStateChanges()
.onEach { _ ->
// Disable swipe refresh when view is not at the top
val firstPos = (it.layoutManager as LinearLayoutManager)
.findFirstCompletelyVisibleItemPosition()
binding.swipeRefresh.isEnabled = firstPos <= 0
}
.launchIn(viewScope)
binding.fastScroller.doOnLayout { scroller ->
scroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = getMainAppBarHeight()
}
scroller.applyInsetter {
type(navigationBars = true) {
margin()
}
}
}
binding.swipeRefresh.doOnLayout { swipeRefresh ->
swipeRefresh as SwipeRefreshLayout
swipeRefresh.setOnApplyWindowInsetsListener { _, windowInsets ->
val topStatusBarInset = WindowInsetsCompat.toWindowInsetsCompat(windowInsets)
.getInsets(WindowInsetsCompat.Type.statusBars())
.top
swipeRefresh.setProgressViewEndTarget(false, getMainAppBarHeight() + topStatusBarInset)
windowInsets
}
}
}
// Tablet layout
binding.infoRecycler?.let {
@@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.ui.manga.info
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import androidx.recyclerview.widget.RecyclerView
import coil.loadAny
import coil.target.ImageViewTarget
@@ -16,6 +18,7 @@ import eu.kanade.tachiyomi.source.Source
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.base.controller.getMainAppBarHeight
import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.util.system.copyToClipboard
import eu.kanade.tachiyomi.util.view.setChips
@@ -47,6 +50,7 @@ class MangaInfoHeaderAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HeaderViewHolder {
binding = MangaInfoHeaderBinding.inflate(LayoutInflater.from(parent.context), parent, false)
updateCoverPosition()
return HeaderViewHolder(binding.root)
}
@@ -75,6 +79,15 @@ class MangaInfoHeaderAdapter(
notifyDataSetChanged()
}
private fun updateCoverPosition() {
val appBarHeight = controller.getMainAppBarHeight()
binding.mangaCover.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin += appBarHeight
}
binding.root.getConstraintSet(R.id.end)
?.setMargin(R.id.manga_cover, ConstraintLayout.LayoutParams.TOP, appBarHeight)
}
inner class HeaderViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind() {
// For rounded corners