Making the recents selector sticky with the app bar

thankfully i've already done logic for the view to pad by  the toolbar + insets instead of just the appbar height.
This commit is contained in:
Jays2Kings 2021-04-07 18:19:37 -04:00
parent 9fbe223970
commit 04ab0d37ea
11 changed files with 136 additions and 78 deletions

View File

@ -34,11 +34,11 @@ interface ChapterQueries : DbProvider {
fun getRecentChapters(date: Date) = getRecentChapters(Date(), date)
fun getRecentChapters(startDate: Date, date: Date) = db.get()
fun getRecentChapters(startDate: Date, date: Date, search: String = "") = db.get()
.listOfObjects(MangaChapter::class.java)
.withQuery(
RawQuery.builder()
.query(getRecentsQuery())
.query(getRecentsQuery(search.sqLite))
.args(date.time, startDate.time)
.observesTables(ChapterTable.TABLE)
.build()

View File

@ -41,7 +41,7 @@ val libraryQuery =
/**
* Query to get the recent chapters of manga from the library up to a date.
*/
fun getRecentsQuery() =
fun getRecentsQuery(search: String) =
"""
SELECT ${Manga.TABLE}.${Manga.COL_URL} as mangaUrl, * FROM ${Manga.TABLE} JOIN ${Chapter.TABLE}
ON ${Manga.TABLE}.${Manga.COL_ID} = ${Chapter.TABLE}.${Chapter.COL_MANGA_ID}
@ -49,6 +49,7 @@ fun getRecentsQuery() =
AND ${Chapter.COL_DATE_UPLOAD} > ?
AND ${Chapter.COL_DATE_UPLOAD} < ?
AND ${Chapter.COL_DATE_FETCH} > ${Manga.COL_DATE_ADDED}
AND lower(${Manga.COL_TITLE}) LIKE '%$search%'
ORDER BY ${Chapter.COL_DATE_UPLOAD} DESC
"""

View File

@ -23,6 +23,7 @@ import androidx.appcompat.graphics.drawable.DrawerArrowDrawable
import androidx.core.content.ContextCompat
import androidx.core.graphics.ColorUtils
import androidx.core.view.GestureDetectorCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import com.bluelinelabs.conductor.Conductor
import com.bluelinelabs.conductor.Controller
@ -645,6 +646,35 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
animationSet?.start()
}
fun showTabBar(show: Boolean, animate: Boolean = true) {
if (animate) {
if (show) {
binding.tabsFrameLayout.alpha = 0f
binding.tabsFrameLayout.isVisible = true
}
val alphaAnimation = ValueAnimator.ofFloat(
binding.tabsFrameLayout.alpha,
if (show) 1f else 0f
)
alphaAnimation.addUpdateListener { valueAnimator ->
binding.tabsFrameLayout.alpha = valueAnimator.animatedValue as Float
}
alphaAnimation.addListener(
EndAnimatorListener {
binding.tabsFrameLayout.isVisible = show
if (!show) {
binding.mainTabs.clearOnTabSelectedListeners()
binding.mainTabs.removeAllTabs()
}
}
)
alphaAnimation.duration = 200
alphaAnimation.start()
} else {
binding.tabsFrameLayout.isVisible = show
}
}
override fun downloadStatusChanged(downloading: Boolean) {
val hasQueue = downloading || downloadManager.hasQueue()
launchUI {

View File

@ -54,8 +54,6 @@ class RecentMangaAdapter(val delegate: RecentsInterface) :
fun onRemoveHistoryClicked(position: Int)
fun markAsRead(position: Int)
fun isSearching(): Boolean
fun setViewType(viewType: Int)
fun getViewType(): Int
fun scope(): CoroutineScope
}

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.recents
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.tabs.TabLayout
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.davidea.flexibleadapter.items.AbstractHeaderItem
import eu.davidea.flexibleadapter.items.IFlexible
@ -10,7 +9,6 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.databinding.RecentsHeaderItemBinding
import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder
import eu.kanade.tachiyomi.ui.library.LibraryHeaderItem
import eu.kanade.tachiyomi.util.view.visibleIf
class RecentMangaHeaderItem(val recentsType: Int) :
AbstractHeaderItem<RecentMangaHeaderItem.Holder>() {
@ -62,21 +60,6 @@ class RecentMangaHeaderItem(val recentsType: Int) :
) {
private val binding = RecentsHeaderItemBinding.bind(view)
init {
listOf(R.string.grouped, R.string.all, R.string.history, R.string.updates).forEach { resId ->
binding.recentsTabs.addTab(binding.recentsTabs.newTab().setText(resId))
}
val selectedTab = (this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.getViewType() ?: 0
binding.recentsTabs.selectTab(binding.recentsTabs.getTabAt(selectedTab))
binding.recentsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
(this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.setViewType(tab?.position ?: 0)
}
override fun onTabUnselected(tab: TabLayout.Tab?) { }
override fun onTabReselected(tab: TabLayout.Tab?) { }
})
}
fun bind(recentsType: Int) {
binding.title.setText(
@ -87,10 +70,6 @@ class RecentMangaHeaderItem(val recentsType: Int) :
else -> R.string.continue_reading
}
)
binding.recentsTabs.visibleIf(recentsType == -1)
val selectedTab = (this@Holder.bindingAdapter as? RecentMangaAdapter)?.delegate?.getViewType() ?: 0
binding.recentsTabs.selectTab(binding.recentsTabs.getTabAt(selectedTab))
binding.title.visibleIf(recentsType != -1)
}
}

View File

@ -10,6 +10,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.widget.SearchView
import androidx.core.view.isVisible
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
@ -18,6 +19,7 @@ import com.bluelinelabs.conductor.ControllerChangeType
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayout
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
@ -53,6 +55,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
/**
* Fragment that shows recently read manga.
@ -129,8 +132,9 @@ class RecentsController(bundle: Bundle? = null) :
scrollViewWith(
binding.recycler,
swipeRefreshLayout = binding.swipeRefresh,
includeTabView = true,
afterInsets = {
headerHeight = it.systemWindowInsetTop + appBarHeight
headerHeight = it.systemWindowInsetTop + appBarHeight + 48.dpToPx
binding.recycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
binding.downloadBottomSheet.dlRecycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
},
@ -142,12 +146,12 @@ class RecentsController(bundle: Bundle? = null) :
activityBinding?.bottomNav?.post {
binding.recycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
binding.downloadBottomSheet.dlRecycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
activityBinding?.tabsFrameLayout?.isVisible = !binding.downloadBottomSheet.root.sheetBehavior.isExpanded()
}
presenter.onCreate()
if (presenter.recentItems.isNotEmpty()) {
adapter.updateDataSet(presenter.recentItems)
adapter.addScrollableHeader(presenter.generalHeader)
}
binding.downloadBottomSheet.dlBottomSheet.onCreate(this)
@ -163,12 +167,24 @@ class RecentsController(bundle: Bundle? = null) :
override fun onSlide(bottomSheet: View, progress: Float) {
binding.shadow2.alpha = (1 - abs(progress)) * 0.25f
binding.shadow.alpha = (1 - abs(progress)) * 0.5f
if (progress >= 0) activityBinding?.appBar?.elevation = max(
progress * 15f,
val height = binding.root.height - binding.downloadBottomSheet.dlRecycler.paddingTop
// Doing some fun math to hide the tab bar just as the title text of the
// dl sheet is under the toolbar
val cap = height * (1 / 12600f) + 479f / 700
activityBinding?.appBar?.elevation = min(
(1f - progress / cap) * 15f,
if (binding.recycler.canScrollVertically(-1)) 15f else 0f
)
binding.downloadBottomSheet.sheetLayout.alpha = 1 - progress
).coerceIn(0f, 15f)
binding.downloadBottomSheet.sheetLayout.alpha = 1 - max(0f, progress / cap)
activityBinding?.appBar?.y = max(activityBinding!!.appBar.y, -headerHeight * (1 - progress))
activityBinding?.tabsFrameLayout?.let { tabs ->
tabs.alpha = 1 - max(0f, progress / cap)
if (tabs.alpha <= 0 && tabs.isVisible) {
tabs.isVisible = false
} else if (tabs.alpha > 0 && !tabs.isVisible) {
tabs.isVisible = true
}
}
val oldShow = showingDownloads
showingDownloads = progress > 0.92f
if (oldShow != showingDownloads) {
@ -188,6 +204,7 @@ class RecentsController(bundle: Bundle? = null) :
activity?.invalidateOptionsMenu()
}
activityBinding?.tabsFrameLayout?.isVisible = state != BottomSheetBehavior.STATE_EXPANDED
if (state == BottomSheetBehavior.STATE_COLLAPSED) {
if (hasQueue()) {
binding.downloadBottomSheet.dlBottomSheet.sheetBehavior?.isHideable = false
@ -229,7 +246,7 @@ class RecentsController(bundle: Bundle? = null) :
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 301)
}
fun setBottomPadding() {
private fun setBottomPadding() {
val bottomBar = activityBinding?.bottomNav ?: return
val pad = bottomBar.translationY - bottomBar.height
val padding = max(
@ -264,7 +281,7 @@ class RecentsController(bundle: Bundle? = null) :
return true
}
if (presenter.preferences.recentsViewType().get() != presenter.viewType) {
presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_GROUP_ALL, false)
tempJumpTo(RecentsPresenter.VIEW_TYPE_GROUP_ALL)
return true
}
return false
@ -299,15 +316,8 @@ class RecentsController(bundle: Bundle? = null) :
fun showLists(recents: List<RecentMangaItem>, hasNewItems: Boolean, shouldMoveToTop: Boolean = false) {
if (view == null) return
binding.swipeRefresh.isRefreshing = LibraryUpdateService.isRunning()
adapter.removeAllScrollableHeaders()
adapter.updateItems(recents)
adapter.headerItems.forEach {
if (it != presenter.generalHeader || presenter.query.isNotEmpty()) {
adapter.removeScrollableHeader(it)
}
}
if (presenter.query.isEmpty() && !adapter.headerItems.any { it === presenter.generalHeader }) {
adapter.addScrollableHeader(presenter.generalHeader)
}
adapter.onLoadMoreComplete(null)
if (!hasNewItems || presenter.viewType == RecentsPresenter.VIEW_TYPE_GROUP_ALL || presenter.query.isNotEmpty() ||
recents.isEmpty()
@ -370,22 +380,17 @@ class RecentsController(bundle: Bundle? = null) :
onItemLongClick(position)
}
fun showHistory() {
presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_ONLY_HISTORY, false)
private fun tempJumpTo(viewType: Int) {
presenter.toggleGroupRecents(viewType, false)
activityBinding?.mainTabs?.selectTab(activityBinding?.mainTabs?.getTabAt(viewType))
}
fun showUpdates() {
presenter.toggleGroupRecents(RecentsPresenter.VIEW_TYPE_ONLY_UPDATES, false)
}
override fun setViewType(viewType: Int) {
private fun setViewType(viewType: Int) {
if (viewType != presenter.viewType) {
presenter.toggleGroupRecents(viewType)
}
}
override fun getViewType() = presenter.viewType
override fun scope() = adapterScope
override fun onItemClick(view: View?, position: Int): Boolean {
@ -393,11 +398,13 @@ class RecentsController(bundle: Bundle? = null) :
if (item is RecentMangaItem) {
if (item.mch.manga.id == null) {
val headerItem = adapter.getHeaderOf(item) as? RecentMangaHeaderItem
when (headerItem?.recentsType) {
RecentMangaHeaderItem.NEW_CHAPTERS -> showUpdates()
RecentMangaHeaderItem.CONTINUE_READING -> showHistory()
else -> return false
}
tempJumpTo(
when (headerItem?.recentsType) {
RecentMangaHeaderItem.NEW_CHAPTERS -> RecentsPresenter.VIEW_TYPE_ONLY_UPDATES
RecentMangaHeaderItem.CONTINUE_READING -> RecentsPresenter.VIEW_TYPE_ONLY_HISTORY
else -> return false
}
)
} else {
val activity = activity ?: return false
val intent = ReaderActivity.newIntent(activity, item.mch.manga, item.chapter)
@ -495,8 +502,38 @@ class RecentsController(bundle: Bundle? = null) :
if (type.isEnter) {
if (type == ControllerChangeType.POP_ENTER) presenter.onCreate()
binding.downloadBottomSheet.dlBottomSheet.dismiss()
activityBinding?.mainTabs?.let { tabs ->
tabs.removeAllTabs()
tabs.clearOnTabSelectedListeners()
val selectedTab = presenter.preferences.recentsViewType().get()
listOf(
R.string.grouped,
R.string.all,
R.string.history,
R.string.updates
).forEachIndexed { index, resId ->
tabs.addTab(
tabs.newTab().setText(resId).also { tab ->
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
tab.view.tooltipText = null
}
},
index == selectedTab
)
}
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
setViewType(tab?.position ?: 0)
}
override fun onTabUnselected(tab: TabLayout.Tab?) {}
override fun onTabReselected(tab: TabLayout.Tab?) {}
})
(activity as? MainActivity)?.showTabBar(true)
}
} else {
if (type == ControllerChangeType.POP_EXIT) presenter.onDestroy()
(activity as? MainActivity)?.showTabBar(false)
snack?.dismiss()
}
setBottomPadding()

View File

@ -49,7 +49,6 @@ class RecentsPresenter(
}
private val newAdditionsHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEWLY_ADDED)
private val newChaptersHeader = RecentMangaHeaderItem(RecentMangaHeaderItem.NEW_CHAPTERS)
val generalHeader = RecentMangaHeaderItem(-1)
private val continueReadingHeader = RecentMangaHeaderItem(
RecentMangaHeaderItem
.CONTINUE_READING

View File

@ -137,7 +137,7 @@ class BrowseController :
afterInsets = {
headerHeight = it.systemWindowInsetTop + appBarHeight
binding.sourceRecycler.updatePaddingRelative(
top = activityBinding?.appBar?.height ?: 0,
top = headerHeight,
bottom = (activityBinding?.bottomNav?.height ?: 0) + 58.spToPx
)
},

View File

@ -109,19 +109,23 @@ fun Controller.scrollViewWith(
afterInsets: ((WindowInsets) -> Unit)? = null,
liftOnScroll: ((Boolean) -> Unit)? = null,
onLeavingController: (() -> Unit)? = null,
onBottomNavUpdate: (() -> Unit)? = null
onBottomNavUpdate: (() -> Unit)? = null,
includeTabView: Boolean = false
): ((Boolean) -> Unit) {
var statusBarHeight = -1
val tabBarHeight = 48.dpToPx
activityBinding?.appBar?.y = 0f
val attrsArray = intArrayOf(R.attr.actionBarSize)
val array = recycler.context.obtainStyledAttributes(attrsArray)
var appBarHeight = if (activityBinding!!.toolbar.height > 0) activityBinding!!.toolbar.height
else array.getDimensionPixelSize(0, 0)
var appBarHeight = (
if (activityBinding!!.toolbar.height > 0) activityBinding!!.toolbar.height
else array.getDimensionPixelSize(0, 0)
) + if (includeTabView) tabBarHeight else 0
array.recycle()
swipeRefreshLayout?.setDistanceToTriggerSync(150.dpToPx)
activityBinding!!.toolbar.post {
if (activityBinding!!.toolbar.height > 0) {
appBarHeight = activityBinding!!.toolbar.height
appBarHeight = activityBinding!!.toolbar.height + if (includeTabView) tabBarHeight else 0
recycler.requestApplyInsets()
}
}
@ -206,8 +210,10 @@ fun Controller.scrollViewWith(
}
}
} else {
if (!customPadding && lastY == 0f && router.backstack.lastOrNull()
?.controller() is MangaDetailsController
if (!customPadding && lastY == 0f && (
router.backstack.lastOrNull()
?.controller() is MangaDetailsController || includeTabView
)
) {
val parent = recycler.parent as? ViewGroup ?: return
val v = View(activity)

View File

@ -60,6 +60,28 @@
tools:text="Title Text" />
</LinearLayout>
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
<FrameLayout
android:id="@+id/tabs_frame_layout"
android:clickable="true"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:ignore="KeyboardInaccessibleWidget">
<com.google.android.material.tabs.TabLayout
android:id="@+id/main_tabs"
style="@style/Theme.Widget.Tabs.Highlight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tabIndicatorColor="?attr/colorAccent"
app:tabGravity="fill"/>
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout>
<View

View File

@ -20,18 +20,4 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
tools:text="@string/recent_updates" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/recents_tabs"
style="@style/Theme.Widget.Tabs.Highlight"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="6dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:tabIndicatorColor="?attr/colorAccent"
app:tabGravity="fill"/>
</androidx.constraintlayout.widget.ConstraintLayout>