From 04ab0d37ea8adf2afda20d89620bd461895ebf4e Mon Sep 17 00:00:00 2001 From: Jays2Kings Date: Wed, 7 Apr 2021 18:19:37 -0400 Subject: [PATCH] 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. --- .../data/database/queries/ChapterQueries.kt | 4 +- .../data/database/queries/RawQueries.kt | 3 +- .../kanade/tachiyomi/ui/main/MainActivity.kt | 30 ++++++ .../ui/recents/RecentMangaAdapter.kt | 2 - .../ui/recents/RecentMangaHeaderItem.kt | 21 ---- .../tachiyomi/ui/recents/RecentsController.kt | 97 +++++++++++++------ .../tachiyomi/ui/recents/RecentsPresenter.kt | 1 - .../tachiyomi/ui/source/BrowseController.kt | 2 +- .../util/view/ControllerExtensions.kt | 18 ++-- app/src/main/res/layout/main_activity.xml | 22 +++++ .../main/res/layout/recents_header_item.xml | 14 --- 11 files changed, 136 insertions(+), 78 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt index c6eae700e2..058a97d7f2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt @@ -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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt index 2751eff9d0..688621f7f9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/RawQueries.kt @@ -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 """ diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt index 9ce3a8ba10..dd6e06257d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.kt @@ -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(), 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 { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaAdapter.kt index 0e00c09b15..a779d35876 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaAdapter.kt @@ -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 } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHeaderItem.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHeaderItem.kt index 9b3c082e10..07b7e85195 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHeaderItem.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentMangaHeaderItem.kt @@ -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() { @@ -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) } } 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 ef65839f53..c6de61a478 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 @@ -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, 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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt index 64321fa228..e55d28f036 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt @@ -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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt index f492d27c2c..63f464ebb0 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/BrowseController.kt @@ -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 ) }, 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 9e62c6cbe5..a15b15c935 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 @@ -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) diff --git a/app/src/main/res/layout/main_activity.xml b/app/src/main/res/layout/main_activity.xml index 426839a249..5fe8809bab 100644 --- a/app/src/main/res/layout/main_activity.xml +++ b/app/src/main/res/layout/main_activity.xml @@ -60,6 +60,28 @@ tools:text="Title Text" /> + + + + - - \ No newline at end of file