Initial tablet NavigationRailView implementation

Co-Authored-By: arkon <4098258+arkon@users.noreply.github.com>
This commit is contained in:
Jays2Kings 2021-05-23 00:11:32 -04:00
parent 3dce788c94
commit d82d738f38
13 changed files with 535 additions and 167 deletions

View File

@ -0,0 +1,50 @@
package eu.kanade.tachiyomi.ui.base.controller
import android.animation.Animator
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.view.View
import android.view.ViewGroup
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import eu.kanade.tachiyomi.util.system.isTablet
/**
* A variation of [FadeChangeHandler] that only fades in.
*/
class OneWayFadeChangeHandler : FadeChangeHandler {
constructor()
constructor(removesFromViewOnPush: Boolean) : super(removesFromViewOnPush)
constructor(duration: Long) : super(duration)
constructor(duration: Long, removesFromViewOnPush: Boolean) : super(
duration,
removesFromViewOnPush
)
override fun getAnimator(
container: ViewGroup,
from: View?,
to: View?,
isPush: Boolean,
toAddedToContainer: Boolean
): Animator {
val animator = AnimatorSet()
if (to != null) {
val start: Float = if (toAddedToContainer) 0F else to.alpha
animator.play(ObjectAnimator.ofFloat(to, View.ALPHA, start, 1f))
}
if (from != null && (!isPush || removesFromViewOnPush())) {
if (!container.context.isTablet()) {
animator.play(ObjectAnimator.ofFloat(from, View.ALPHA, 0f))
} else {
container.removeView(from)
}
}
return animator
}
override fun copy(): ControllerChangeHandler {
return OneWayFadeChangeHandler(animationDuration, removesFromViewOnPush())
}
}

View File

@ -91,10 +91,10 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
binding.pager.adapter = TabbedSheetAdapter()
binding.tabs.setupWithViewPager(binding.pager)
this.controller = controller
binding.pager.doOnApplyWindowInsets { _, _, _ ->
binding.pager.doOnApplyWindowInsets { _, insets, _ ->
val bottomBar = controller.activityBinding?.bottomNav
extensionFrameLayout?.binding?.recycler?.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
migrationFrameLayout?.binding?.recycler?.updatePaddingRelative(bottom = bottomBar?.height ?: 0)
extensionFrameLayout?.binding?.recycler?.updatePaddingRelative(bottom = bottomBar?.height ?: insets.systemWindowInsetBottom)
migrationFrameLayout?.binding?.recycler?.updatePaddingRelative(bottom = bottomBar?.height ?: insets.systemWindowInsetBottom)
}
binding.tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
@ -336,9 +336,15 @@ class ExtensionBottomSheet @JvmOverloads constructor(context: Context, attrs: At
* @return a new view.
*/
override fun createView(container: ViewGroup): View {
val binding = RecyclerWithScrollerBinding.inflate(LayoutInflater.from(container.context), container, false)
val binding = RecyclerWithScrollerBinding.inflate(
LayoutInflater.from(container.context),
container,
false
)
val view: RecyclerWithScrollerView = binding.root
view.setUp(this@ExtensionBottomSheet, binding, this@ExtensionBottomSheet.controller.activityBinding?.bottomNav?.height ?: 0)
val height = this@ExtensionBottomSheet.controller.activityBinding?.bottomNav?.height
?: view.rootWindowInsets?.systemWindowInsetBottom ?: 0
view.setUp(this@ExtensionBottomSheet, binding, height)
return view
}

View File

@ -200,6 +200,10 @@ class LibraryController(
get() = preferences.showCategoryInTitle().get() && presenter.showAllCategories
private lateinit var elevateAppBar: ((Boolean) -> Unit)
private var hopperOffset = 0f
private val maxHopperOffset: Float
get() =
if (activityBinding?.bottomNav != null) 55f.dpToPx
else (view?.rootWindowInsets?.systemWindowInsetBottom?.toFloat() ?: 0f) + 55f.dpToPx
override fun getTitle(): String? {
setSubtitle()
@ -222,9 +226,9 @@ class LibraryController(
if (!recyclerCover.isClickable && isAnimatingHopper != true) {
if (preferences.autohideHopper().get()) {
hopperOffset += dy
hopperOffset = hopperOffset.coerceIn(0f, 55f.dpToPx)
hopperOffset = hopperOffset.coerceIn(0f, maxHopperOffset)
}
if (!preferences.hideBottomNavOnScroll().get()) {
if (!preferences.hideBottomNavOnScroll().get() || activityBinding?.bottomNav == null) {
updateFilterSheetY()
}
binding.roundedCategoryHopper.upCategory.alpha = if (isAtTop()) 0.25f else 1f
@ -291,6 +295,11 @@ class LibraryController(
binding.fastScroller.updateLayoutParams<ViewGroup.MarginLayoutParams> {
bottomMargin = -pad.toInt()
}
} else {
binding.filterBottomSheet.filterBottomSheet.updatePaddingRelative(
bottom = view?.rootWindowInsets?.getBottomGestureInsets() ?: 0
)
updateHopperY()
}
}
@ -300,12 +309,17 @@ class LibraryController(
) ?: 0
if (preferences.autohideHopper().get()) {
// Flow same snap rules as bottom nav
val closerToHopperBottom = hopperOffset > 25f.dpToPx
val closerToHopperBottom = hopperOffset > maxHopperOffset / 2
val halfWayBottom = activityBinding?.bottomNav?.height?.toFloat()?.div(2) ?: 0f
val closerToBottom = (activityBinding?.bottomNav?.translationY ?: 0f) > halfWayBottom
val atTop = !binding.libraryGridRecycler.recycler.canScrollVertically(-1)
val closerToEdge = if (preferences.hideBottomNavOnScroll().get()) (closerToBottom && !atTop) else closerToHopperBottom
val end = if (closerToEdge) 55f.dpToPx else 0f
val closerToEdge =
if (preferences.hideBottomNavOnScroll().get() && activityBinding?.bottomNav != null) {
closerToBottom && !atTop
} else {
closerToHopperBottom
}
val end = if (closerToEdge) maxHopperOffset else 0f
val alphaAnimation = ValueAnimator.ofFloat(hopperOffset, end)
alphaAnimation.addUpdateListener { valueAnimator ->
hopperOffset = valueAnimator.animatedValue as Float
@ -385,7 +399,7 @@ class LibraryController(
if (preferences.shownFilterTutorial().get() || !hasExpanded) return
val activityBinding = activityBinding ?: return
val activity = activity ?: return
val icon = activityBinding.bottomNav.getItemView(R.id.nav_library) ?: return
val icon = (activityBinding.bottomNav ?: activityBinding.sideNav)?.getItemView(R.id.nav_library) ?: return
filterTooltip =
ViewTooltip.on(activity, icon).autoHide(false, 0L).align(ViewTooltip.ALIGN.START)
.position(ViewTooltip.Position.TOP).text(R.string.tap_library_to_show_filters)
@ -684,7 +698,7 @@ class LibraryController(
activityBinding?.bottomNav?.y ?: binding.filterBottomSheet.filterBottomSheet.y
)
val insetBottom = view.rootWindowInsets?.systemWindowInsetBottom ?: 0
if (!preferences.autohideHopper().get()) {
if (!preferences.autohideHopper().get() || activityBinding?.bottomNav == null) {
listOfYs.add(view.height - (insetBottom).toFloat())
}
binding.categoryHopperFrame.y = -binding.categoryHopperFrame.height +

View File

@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.ui.library.display
import android.animation.ValueAnimator
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewTreeObserver
import android.widget.SeekBar
import androidx.core.animation.addListener
@ -27,6 +28,7 @@ import kotlin.math.roundToInt
class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
BaseLibraryDisplayView<LibraryDisplayLayoutBinding>(context, attrs) {
var mainView: View? = null
override fun inflateBinding() = LibraryDisplayLayoutBinding.bind(this)
override fun initGeneralPreferences() {
binding.displayGroup.bindToPreference(preferences.libraryLayout())
@ -127,7 +129,7 @@ class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: Attr
private fun setGridText(progress: Int) {
with(binding.gridSizeText) {
val rows = this@LibraryDisplayView.rowsForValue(progress)
val rows = (mainView ?: this@LibraryDisplayView).rowsForValue(progress)
val titleText = context.getString(R.string.grid_size)
val subtitleText = context.getString(R.string._per_row, rows)
text = titleText.withSubtitle(context, subtitleText)
@ -138,7 +140,7 @@ class LibraryDisplayView @JvmOverloads constructor(context: Context, attrs: Attr
with(binding.seekBarTextView.root) {
val value =
(progress * (seekBar.width - 12.dpToPx - 2 * seekBar.thumbOffset)) / seekBar.max
text = this@LibraryDisplayView.rowsForValue(progress).toString()
text = (mainView ?: this@LibraryDisplayView).rowsForValue(progress).toString()
x = seekBar.x + value + seekBar.thumbOffset / 2 + 5.dpToPx
y = seekBar.y + binding.gridSizeLayout.y - 6.dpToPx - height
}

View File

@ -25,6 +25,7 @@ open class TabbedLibraryDisplaySheet(val controller: Controller) :
badgesView.controller = libraryController
categoryView.controller = libraryController
}
displayView.mainView = controller.view
binding.menu.isVisible = controller !is SettingsLibraryController
binding.menu.compatToolTipText = context.getString(R.string.more_library_settings)
binding.menu.setImageDrawable(

View File

@ -110,7 +110,10 @@ class FilterBottomSheet @JvmOverloads constructor(context: Context, attrs: Attri
this.controller = controller
libraryRecyler = controller.binding.libraryGridRecycler.recycler
libraryRecyler?.post {
bottomBarHeight = controller.activityBinding?.bottomNav?.height ?: 0
bottomBarHeight =
controller.activityBinding?.bottomNav?.height
?: controller.activityBinding?.root?.rootWindowInsets?.systemWindowInsetBottom
?: 0
}
val shadow2: View = controller.binding.shadow2
val shadow: View = controller.binding.shadow

View File

@ -35,6 +35,7 @@ import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.Router
import com.getkeepsafe.taptargetview.TapTarget
import com.getkeepsafe.taptargetview.TapTargetView
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.Migrations
@ -71,6 +72,7 @@ import eu.kanade.tachiyomi.util.system.contextCompatDrawable
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.hasSideNavBar
import eu.kanade.tachiyomi.util.system.isBottomTappable
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.view.doOnApplyWindowInsets
@ -167,7 +169,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
)
var continueSwitchingTabs = false
binding.bottomNav.getItemView(R.id.nav_library)?.setOnLongClickListener {
nav.getItemView(R.id.nav_library)?.setOnLongClickListener {
if (!LibraryUpdateService.isRunning()) {
LibraryUpdateService.start(this)
binding.mainContent.snack(R.string.updating_library) {
@ -186,9 +188,9 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
true
}
for (id in listOf(R.id.nav_recents, R.id.nav_browse)) {
binding.bottomNav.getItemView(id)?.setOnLongClickListener {
binding.bottomNav.selectedItemId = id
binding.bottomNav.post {
nav.getItemView(id)?.setOnLongClickListener {
nav.selectedItemId = id
nav.post {
val controller =
router.backstack.firstOrNull()?.controller as? BottomSheetController
controller?.showSheet()
@ -196,15 +198,67 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
true
}
}
binding.bottomNav.setOnNavigationItemSelectedListener { item ->
val container: ViewGroup = binding.controllerContainer
val content: ViewGroup = binding.mainContent
DownloadService.addListener(this)
content.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
container.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
supportActionBar?.setDisplayShowCustomEnabled(true)
setNavBarColor(content.rootWindowInsets)
nav.isVisible = false
content.doOnApplyWindowInsets { v, insets, _ ->
setNavBarColor(insets)
val contextView = window?.decorView?.findViewById<View>(R.id.action_mode_bar)
contextView?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.systemWindowInsetLeft
rightMargin = insets.systemWindowInsetRight
}
// Consume any horizontal insets and pad all content in. There's not much we can do
// with horizontal insets
v.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
binding.appBar.updatePadding(
top = insets.systemWindowInsetTop
)
binding.bottomNav?.updatePadding(bottom = insets.systemWindowInsetBottom)
binding.sideNav?.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
binding.bottomView?.isVisible = insets.systemWindowInsetBottom > 0
binding.bottomView?.updateLayoutParams<ViewGroup.LayoutParams> {
height = insets.systemWindowInsetBottom
}
}
router = Conductor.attachRouter(this, container, savedInstanceState)
if (router.hasRootController()) {
nav.selectedItemId =
when (router.backstack.firstOrNull()?.controller) {
is RecentsController -> R.id.nav_recents
is BrowseController -> R.id.nav_browse
else -> R.id.nav_library
}
}
nav.setOnItemSelectedListener { item ->
val id = item.itemId
val currentController = router.backstack.lastOrNull()?.controller
if (!continueSwitchingTabs && currentController is BottomNavBarInterface) {
if (!currentController.canChangeTabs {
continueSwitchingTabs = true
this@MainActivity.binding.bottomNav.selectedItemId = id
this@MainActivity.nav.selectedItemId = id
}
) return@setOnNavigationItemSelectedListener false
) return@setOnItemSelectedListener false
}
continueSwitchingTabs = false
val currentRoot = router.backstack.firstOrNull()
@ -226,43 +280,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
true
}
val container: ViewGroup = binding.controllerContainer
val content: ViewGroup = binding.mainContent
DownloadService.addListener(this)
content.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
container.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
supportActionBar?.setDisplayShowCustomEnabled(true)
setNavBarColor(content.rootWindowInsets)
binding.bottomView.isVisible = false
content.doOnApplyWindowInsets { v, insets, _ ->
setNavBarColor(insets)
val contextView = window?.decorView?.findViewById<View>(R.id.action_mode_bar)
contextView?.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = insets.systemWindowInsetLeft
rightMargin = insets.systemWindowInsetRight
}
// Consume any horizontal insets and pad all content in. There's not much we can do
// with horizontal insets
v.updatePadding(
left = insets.systemWindowInsetLeft,
right = insets.systemWindowInsetRight
)
binding.appBar.updatePadding(
top = insets.systemWindowInsetTop
)
binding.bottomNav.updatePadding(bottom = insets.systemWindowInsetBottom)
binding.bottomView.isVisible = insets.systemWindowInsetBottom > 0
binding.bottomView.updateLayoutParams<ViewGroup.LayoutParams> {
height = insets.systemWindowInsetBottom
}
}
router = Conductor.attachRouter(this, container, savedInstanceState)
if (!router.hasRootController()) {
// Set start screen
if (!handleIntentAction(intent)) {
@ -288,9 +306,9 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
binding.cardToolbar.menu.findItem(R.id.action_search)?.expandActionView()
}
binding.bottomNav.isVisible = !hideBottomNav
binding.bottomView.visibility = if (hideBottomNav) View.GONE else binding.bottomView.visibility
binding.bottomNav.alpha = if (hideBottomNav) 0f else 1f
nav.isVisible = !hideBottomNav
binding.bottomView?.visibility = if (hideBottomNav) View.GONE else binding.bottomView?.visibility ?: View.GONE
nav.alpha = if (hideBottomNav) 0f else 1f
router.addChangeListener(
object : ControllerChangeHandler.ControllerChangeListener {
override fun onChangeStarted(
@ -303,7 +321,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
syncActivityViewWithController(to, from, isPush)
binding.appBar.y = 0f
if (!isPush || router.backstackSize == 1) {
binding.bottomNav.translationY = 0f
nav.translationY = 0f
}
snackBar?.dismiss()
}
@ -316,7 +334,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
handler: ControllerChangeHandler
) {
binding.appBar.y = 0f
binding.bottomNav.translationY = 0f
nav.translationY = 0f
showDLQueueTutorial()
}
}
@ -348,10 +366,10 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
binding.cardToolbar.setIncognitoMode(it)
}
setExtensionsBadge()
setFloatingToolbar(canShowFloatingToolbar(router.backstack.lastOrNull()?.controller))
setFloatingToolbar(canShowFloatingToolbar(router.backstack.lastOrNull()?.controller), changeBG = false)
}
open fun setFloatingToolbar(show: Boolean, solidBG: Boolean = false) {
open fun setFloatingToolbar(show: Boolean, solidBG: Boolean = false, changeBG: Boolean = true) {
val oldTB = currentToolbar
currentToolbar = if (show) {
binding.cardToolbar
@ -363,9 +381,11 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
binding.toolbar.isVisible = !show
binding.cardFrame.isVisible = show
if (changeBG) {
binding.appBar.setBackgroundColor(
if (show && !solidBG) Color.TRANSPARENT else getResourceColor(R.attr.colorSecondary)
)
}
currentToolbar?.setNavigationOnClickListener {
val rootSearchController = router.backstack.lastOrNull()?.controller
if (rootSearchController is RootSearchInterface) {
@ -435,10 +455,10 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
private fun setExtensionsBadge() {
val updates = preferences.extensionUpdatesCount().getOrDefault()
if (updates > 0) {
val badge = binding.bottomNav.getOrCreateBadge(R.id.nav_browse)
val badge = nav.getOrCreateBadge(R.id.nav_browse)
badge.number = updates
} else {
binding.bottomNav.removeBadge(R.id.nav_browse)
nav.removeBadge(R.id.nav_browse)
}
}
@ -455,7 +475,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
downloadManager.hasQueue() && !preferences.shownDownloadQueueTutorial().get()
) {
if (!isBindingInitialized) return
val recentsItem = binding.bottomNav.getItemView(R.id.nav_recents) ?: return
val recentsItem = nav.getItemView(R.id.nav_recents) ?: return
preferences.shownDownloadQueueTutorial().set(true)
TapTargetView.showFor(
this,
@ -474,7 +494,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
object : TapTargetView.Listener() {
override fun onTargetClick(view: TapTargetView) {
super.onTargetClick(view)
binding.bottomNav.selectedItemId = R.id.nav_recents
nav.selectedItemId = R.id.nav_recents
}
}
)
@ -540,14 +560,14 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
intent.getIntExtra("groupId", 0)
)
when (intent.action) {
SHORTCUT_LIBRARY -> binding.bottomNav.selectedItemId = R.id.nav_library
SHORTCUT_LIBRARY -> nav.selectedItemId = R.id.nav_library
SHORTCUT_RECENTLY_UPDATED, SHORTCUT_RECENTLY_READ -> {
if (binding.bottomNav.selectedItemId != R.id.nav_recents) {
binding.bottomNav.selectedItemId = R.id.nav_recents
if (nav.selectedItemId != R.id.nav_recents) {
nav.selectedItemId = R.id.nav_recents
} else {
router.popToRoot()
}
binding.bottomNav.post {
nav.post {
val controller =
router.backstack.firstOrNull()?.controller as? RecentsController
controller?.tempJumpTo(
@ -558,14 +578,14 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
)
}
}
SHORTCUT_BROWSE -> binding.bottomNav.selectedItemId = R.id.nav_browse
SHORTCUT_BROWSE -> nav.selectedItemId = R.id.nav_browse
SHORTCUT_EXTENSIONS -> {
if (binding.bottomNav.selectedItemId != R.id.nav_browse) {
binding.bottomNav.selectedItemId = R.id.nav_browse
if (nav.selectedItemId != R.id.nav_browse) {
nav.selectedItemId = R.id.nav_browse
} else {
router.popToRoot()
}
binding.bottomNav.post {
nav.post {
val controller =
router.backstack.firstOrNull()?.controller as? BrowseController
controller?.showSheet()
@ -573,25 +593,25 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
SHORTCUT_MANGA -> {
val extras = intent.extras ?: return false
if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library
if (router.backstack.isEmpty()) nav.selectedItemId = R.id.nav_library
router.pushController(MangaDetailsController(extras).withFadeTransaction())
}
SHORTCUT_UPDATE_NOTES -> {
val extras = intent.extras ?: return false
if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library
if (router.backstack.isEmpty()) nav.selectedItemId = R.id.nav_library
if (router.backstack.lastOrNull()?.controller !is AboutController.NewUpdateDialogController) {
AboutController.NewUpdateDialogController(extras).showDialog(router)
}
}
SHORTCUT_SOURCE -> {
val extras = intent.extras ?: return false
if (router.backstack.isEmpty()) binding.bottomNav.selectedItemId = R.id.nav_library
if (router.backstack.isEmpty()) nav.selectedItemId = R.id.nav_library
router.pushController(BrowseSourceController(extras).withFadeTransaction())
}
SHORTCUT_DOWNLOADS -> {
binding.bottomNav.selectedItemId = R.id.nav_recents
nav.selectedItemId = R.id.nav_recents
router.popToRoot()
binding.bottomNav.post {
nav.post {
val controller =
router.backstack.firstOrNull()?.controller as? RecentsController
controller?.showSheet()
@ -619,7 +639,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
else !router.handleBack()
) {
if (preferences.backReturnsToStart().get() && this !is SearchActivity &&
startingTab() != binding.bottomNav.selectedItemId
startingTab() != nav.selectedItemId
) {
goToStartingTab()
} else {
@ -633,13 +653,16 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
}
protected val nav: NavigationBarView
get() = binding.bottomNav ?: binding.sideNav!!
private fun setStartingTab() {
if (this is SearchActivity) return
if (binding.bottomNav.selectedItemId != R.id.nav_browse &&
if (nav.selectedItemId != R.id.nav_browse &&
preferences.startingTab().get() >= 0
) {
preferences.startingTab().set(
when (binding.bottomNav.selectedItemId) {
when (nav.selectedItemId) {
R.id.nav_library -> 0
else -> 1
}
@ -658,7 +681,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
}
private fun goToStartingTab() {
binding.bottomNav.selectedItemId = startingTab()
nav.selectedItemId = startingTab()
}
private fun setRoot(controller: Controller, id: Int) {
@ -750,21 +773,26 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
binding.cardToolbar.subtitle = null
drawerArrow?.progress = 1f
binding.bottomNav.visibility = if (!hideBottomNav) View.VISIBLE else binding.bottomNav.visibility
nav.visibility = if (!hideBottomNav) View.VISIBLE else nav.visibility
if (isTablet()) {
nav.isVisible = !hideBottomNav
nav.alpha = 1f
} else {
animationSet?.cancel()
animationSet = AnimatorSet()
val alphaAnimation = ValueAnimator.ofFloat(
binding.bottomNav.alpha,
nav.alpha,
if (hideBottomNav) 0f else 1f
)
alphaAnimation.addUpdateListener { valueAnimator ->
binding.bottomNav.alpha = valueAnimator.animatedValue as Float
nav.alpha = valueAnimator.animatedValue as Float
}
alphaAnimation.addListener(
EndAnimatorListener {
binding.bottomNav.isVisible = !hideBottomNav
binding.bottomView.visibility =
if (hideBottomNav) View.GONE else binding.bottomView.visibility
nav.isVisible = !hideBottomNav
binding.bottomView?.visibility =
if (hideBottomNav) View.GONE else binding.bottomView?.visibility
?: View.GONE
}
)
alphaAnimation.duration = 200
@ -772,6 +800,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
animationSet?.playTogether(alphaAnimation)
animationSet?.start()
}
}
fun showTabBar(show: Boolean, animate: Boolean = true) {
tabAnimation?.cancel()
@ -810,10 +839,10 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
val hasQueue = downloading || downloadManager.hasQueue()
launchUI {
if (hasQueue) {
binding.bottomNav.getOrCreateBadge(R.id.nav_recents)
nav.getOrCreateBadge(R.id.nav_recents)
showDLQueueTutorial()
} else {
binding.bottomNav.removeBadge(R.id.nav_recents)
nav.removeBadge(R.id.nav_recents)
}
}
}
@ -867,7 +896,7 @@ open class MainActivity : BaseActivity<MainActivityBinding>(), DownloadServiceLi
val diffX = e2.x - e1.x
if (abs(diffX) <= abs(diffY)) {
val sheetRect = Rect()
binding.bottomNav.getGlobalVisibleRect(sheetRect)
binding.bottomNav?.getGlobalVisibleRect(sheetRect)
if (sheetRect.contains(e1.x.toInt(), e1.y.toInt()) &&
abs(diffY) > Companion.SWIPE_THRESHOLD &&
abs(velocityY) > Companion.SWIPE_VELOCITY_THRESHOLD &&

View File

@ -51,8 +51,8 @@ class SearchActivity : MainActivity() {
}
}
override fun setFloatingToolbar(show: Boolean, solidBG: Boolean) {
super.setFloatingToolbar(show, solidBG)
override fun setFloatingToolbar(show: Boolean, solidBG: Boolean, changeBG: Boolean) {
super.setFloatingToolbar(show, solidBG, changeBG)
currentToolbar?.setNavigationOnClickListener { popToRoot() }
}
@ -73,8 +73,8 @@ class SearchActivity : MainActivity() {
binding.toolbar.navigationIcon = drawerArrow
drawerArrow?.progress = 1f
binding.bottomNav.isVisible = false
binding.bottomView.isVisible = false
nav.isVisible = false
binding.bottomView?.isVisible = false
}
override fun handleIntentAction(intent: Intent): Boolean {

View File

@ -163,11 +163,11 @@ class RecentsController(bundle: Bundle? = null) :
height = it.systemWindowInsetTop + (toolbarHeight ?: appBarHeight)
}
binding.recycler.updatePaddingRelative(
bottom = activityBinding?.bottomNav?.height ?: 0
bottom = activityBinding?.bottomNav?.height ?: it.systemWindowInsetBottom
)
binding.recentsEmptyView.updateLayoutParams<ViewGroup.MarginLayoutParams> {
topMargin = headerHeight
bottomMargin = activityBinding?.bottomNav?.height ?: 0
bottomMargin = activityBinding?.bottomNav?.height ?: it.systemWindowInsetBottom
}
},
onBottomNavUpdate = {
@ -182,10 +182,11 @@ class RecentsController(bundle: Bundle? = null) :
}
)
activityBinding?.bottomNav?.post {
binding.recycler.updatePaddingRelative(bottom = activityBinding?.bottomNav?.height ?: 0)
activityBinding?.root?.post {
val height = activityBinding?.bottomNav?.height ?: view.rootWindowInsets?.systemWindowInsetBottom ?: 0
binding.recycler.updatePaddingRelative(bottom = height)
binding.downloadBottomSheet.dlRecycler.updatePaddingRelative(
bottom = activityBinding?.bottomNav?.height ?: 0
bottom = height
)
val isExpanded = binding.downloadBottomSheet.root.sheetBehavior.isExpanded()
activityBinding?.tabsFrameLayout?.isVisible = !isExpanded

View File

@ -22,17 +22,18 @@ import com.bluelinelabs.conductor.Controller
import com.bluelinelabs.conductor.ControllerChangeHandler
import com.bluelinelabs.conductor.ControllerChangeType
import com.bluelinelabs.conductor.RouterTransaction
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
import eu.kanade.tachiyomi.databinding.MainActivityBinding
import eu.kanade.tachiyomi.ui.base.MaterialFastScroll
import eu.kanade.tachiyomi.ui.base.controller.OneWayFadeChangeHandler
import eu.kanade.tachiyomi.ui.main.BottomSheetController
import eu.kanade.tachiyomi.ui.main.FloatingSearchInterface
import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.toast
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
@ -171,6 +172,9 @@ fun Controller.scrollViewWith(
var statusBarHeight = -1
val tabBarHeight = 48.dpToPx
activityBinding?.appBar?.y = 0f
activityBinding?.tabsFrameLayout?.elevation = 0f
val isTabletWithTabs = recycler.context.isTablet() && includeTabView
activityBinding?.tabShadow?.isVisible = isTabletWithTabs
val attrsArray = intArrayOf(R.attr.actionBarSize)
val array = recycler.context.obtainStyledAttributes(attrsArray)
var appBarHeight = (
@ -226,18 +230,34 @@ fun Controller.scrollViewWith(
liftOnScroll.invoke(el)
} else {
elevationAnim?.cancel()
val floatingBar = (this as? FloatingSearchInterface)?.showFloatingBar() == true && !includeTabView
if (isTabletWithTabs && el) {
activityBinding?.tabShadow?.isVisible = true
}
val floatingBar =
(this as? FloatingSearchInterface)?.showFloatingBar() == true && !includeTabView
if (floatingBar) {
if (isTabletWithTabs) {
activityBinding?.tabShadow?.alpha = 0f
} else {
activityBinding?.appBar?.elevation = 0f
}
return@f
}
elevationAnim = ValueAnimator.ofFloat(
activityBinding?.appBar?.elevation ?: 0f,
if (isTabletWithTabs) {
(activityBinding?.tabShadow?.alpha ?: 0f) * 100
} else {
activityBinding?.appBar?.elevation ?: 0f
},
if (el) 15f else 0f
)
elevationAnim?.addUpdateListener { valueAnimator ->
if (isTabletWithTabs) {
activityBinding?.tabShadow?.alpha = valueAnimator.animatedValue as Float / 100
} else {
activityBinding?.appBar?.elevation = valueAnimator.animatedValue as Float
}
}
elevationAnim?.start()
}
}
@ -254,6 +274,7 @@ fun Controller.scrollViewWith(
super.onChangeStart(controller, changeHandler, changeType)
isInView = changeType.isEnter
if (changeType.isEnter) {
activityBinding?.tabShadow?.isVisible = isTabletWithTabs
elevateFunc(elevate)
if (fakeToolbarView?.parent != null) {
val parent = fakeToolbarView?.parent as? ViewGroup ?: return
@ -275,6 +296,7 @@ fun Controller.scrollViewWith(
}
}
} else {
activityBinding?.tabShadow?.isVisible = false
if (!customPadding && lastY == 0f && (
(
this@scrollViewWith !is FloatingSearchInterface && router.backstack.lastOrNull()
@ -318,6 +340,7 @@ fun Controller.scrollViewWith(
recycler.post {
elevateFunc(recycler.canScrollVertically(-1))
}
val isTablet = recycler.context.isTablet()
recycler.addOnScrollListener(
object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
@ -336,7 +359,7 @@ fun Controller.scrollViewWith(
.setDuration(shortAnimationDuration.toLong())
.start()
if (router.backstackSize == 1 && isInView) {
activityBinding!!.bottomNav.let {
activityBinding!!.bottomNav?.let {
val animator = it.animate()?.translationY(0f)
?.setDuration(shortAnimationDuration.toLong())
animator?.setUpdateListener {
@ -348,27 +371,30 @@ fun Controller.scrollViewWith(
lastY = 0f
if (elevate) elevateFunc(false)
} else {
if (!isTablet) {
activityBinding!!.appBar.y -= dy
activityBinding!!.appBar.y = MathUtils.clamp(
activityBinding!!.appBar.y,
-activityBinding!!.appBar.height.toFloat(),
0f
)
val tabBar = activityBinding!!.bottomNav
if (tabBar.isVisible && isInView) {
activityBinding!!.bottomNav?.let { bottomNav ->
if (bottomNav.isVisible && isInView) {
if (preferences.hideBottomNavOnScroll().get()) {
tabBar.translationY += dy
tabBar.translationY = MathUtils.clamp(
tabBar.translationY,
bottomNav.translationY += dy
bottomNav.translationY = MathUtils.clamp(
bottomNav.translationY,
0f,
tabBar.height.toFloat()
bottomNav.height.toFloat()
)
updateViewsNearBottom()
} else if (tabBar.translationY != 0f) {
tabBar.translationY = 0f
} else if (bottomNav.translationY != 0f) {
bottomNav.translationY = 0f
activityBinding!!.bottomView?.translationY = 0f
}
}
}
if (!elevate && (
dy == 0 ||
(
@ -379,6 +405,10 @@ fun Controller.scrollViewWith(
) {
elevateFunc(true)
}
} else {
val notAtTop = recycler.canScrollVertically(-1)
if (notAtTop != elevate) elevateFunc(notAtTop)
}
lastY = activityBinding!!.appBar.y
}
}
@ -387,6 +417,9 @@ fun Controller.scrollViewWith(
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
if (isTablet) {
return
}
if (router?.backstack?.lastOrNull()
?.controller == this@scrollViewWith && statusBarHeight > -1 &&
activity != null && activityBinding!!.appBar.height > 0 &&
@ -397,18 +430,20 @@ fun Controller.scrollViewWith(
android.R.integer.config_shortAnimTime
) ?: 0
val closerToTop = abs(activityBinding!!.appBar.y) > halfWay
val halfWayBottom = activityBinding!!.bottomNav.height.toFloat() / 2
val closerToBottom = activityBinding!!.bottomNav.translationY > halfWayBottom
val halfWayBottom = (activityBinding!!.bottomNav?.height?.toFloat() ?: 0f) / 2
val closerToBottom = activityBinding!!.bottomNav?.translationY ?: 0f > halfWayBottom
val atTop = !recycler.canScrollVertically(-1)
val closerToEdge =
if (activityBinding!!.bottomNav.isVisible &&
if (activityBinding!!.bottomNav?.isVisible == true &&
preferences.hideBottomNavOnScroll().get()
) closerToBottom else closerToTop
lastY =
if (closerToEdge && !atTop) (-activityBinding!!.appBar.height.toFloat()) else 0f
activityBinding!!.appBar.animate().y(lastY)
.setDuration(shortAnimationDuration.toLong()).start()
if (activityBinding!!.bottomNav.isVisible && isInView && preferences.hideBottomNavOnScroll().get()) {
if (activityBinding!!.bottomNav?.isVisible == true &&
isInView && preferences.hideBottomNavOnScroll().get()
) {
activityBinding!!.bottomNav?.let {
val lastBottomY =
if (closerToEdge && !atTop) it.height.toFloat() else 0f
@ -447,8 +482,8 @@ fun Controller.requestPermissionsSafe(permissions: Array<String>, requestCode: I
fun Controller.withFadeTransaction(): RouterTransaction {
return RouterTransaction.with(this)
.pushChangeHandler(FadeChangeHandler())
.popChangeHandler(FadeChangeHandler())
.pushChangeHandler(OneWayFadeChangeHandler())
.popChangeHandler(OneWayFadeChangeHandler())
}
fun Controller.openInBrowser(url: String) {

View File

@ -31,10 +31,10 @@ import androidx.recyclerview.widget.LinearSmoothScroller
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.SmoothScroller
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.bottomnavigation.BottomNavigationItemView
import com.google.android.material.bottomnavigation.BottomNavigationMenuView
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.button.MaterialButton
import com.google.android.material.navigation.NavigationBarItemView
import com.google.android.material.navigation.NavigationBarMenuView
import com.google.android.material.navigation.NavigationBarView
import com.google.android.material.snackbar.Snackbar
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.util.lang.tintText
@ -259,9 +259,9 @@ fun TextView.setTextColorRes(@ColorRes id: Int) {
}
@SuppressLint("RestrictedApi")
fun BottomNavigationView.getItemView(@IdRes id: Int): BottomNavigationItemView? {
fun NavigationBarView.getItemView(@IdRes id: Int): NavigationBarItemView? {
val order = (menu as MenuBuilder).findItemIndex(id)
return (getChildAt(0) as BottomNavigationMenuView).getChildAt(order) as? BottomNavigationItemView
return (getChildAt(0) as NavigationBarMenuView).getChildAt(order) as? NavigationBarItemView
}
fun RecyclerView.smoothScrollToTop() {

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="180"
android:endColor="@android:color/transparent"
android:startColor="@color/md_black_1000_54"/>
</shape>

View File

@ -0,0 +1,220 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="@+id/controller_container"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@id/side_nav"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0">
</com.bluelinelabs.conductor.ChangeHandlerFrameLayout>
<com.google.android.material.navigationrail.NavigationRailView
android:id="@+id/side_nav"
style="@style/Widget.MaterialComponents.NavigationRailView.Colored"
android:background="?android:colorBackground"
android:layout_width="wrap_content"
android:layout_height="0dp"
app:itemIconTint="@color/bottom_nav_item_selector"
app:itemRippleColor="@color/fullRippleColor"
app:itemTextColor="@color/bottom_nav_item_selector"
app:labelVisibilityMode="labeled"
android:translationZ="-10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/app_bar"
app:menu="@menu/bottom_navigation" />
<View
android:id="@+id/shadow_gap"
android:layout_width="1dp"
android:layout_height="16dp"
app:layout_constraintEnd_toEndOf="@id/side_nav"
app:layout_constraintBottom_toTopOf="@id/side_nav" />
<View
android:id="@+id/side_shadow"
android:layout_width="6dp"
android:layout_height="0dp"
android:layout_gravity="bottom"
android:alpha="0.13"
android:translationZ="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@id/side_nav"
app:layout_constraintTop_toTopOf="@id/shadow_gap"
android:background="@drawable/shape_gradient_start_shadow" />
<FrameLayout
android:id="@+id/tabs_frame_layout"
android:clickable="true"
android:visibility="gone"
android:layout_width="0dp"
android:background="?attr/colorSecondary"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/app_bar"
app:layout_constraintStart_toEndOf="@id/side_nav"
app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible"
tools:ignore="KeyboardInaccessibleWidget">
<com.google.android.material.tabs.TabLayout
android:id="@+id/main_tabs"
style="@style/Theme.Widget.Tabs.Highlight"
android:background="?attr/colorSecondary"
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:tabGravity="fill"/>
</FrameLayout>
<View
android:id="@+id/tab_shadow"
android:layout_width="0dp"
android:visibility="gone"
android:layout_height="6dp"
android:rotation="180"
android:alpha="0.15"
android:background="@drawable/shape_gradient_top_shadow"
app:layout_constraintStart_toStartOf="@id/tabs_frame_layout"
app:layout_constraintEnd_toEndOf="@id/tabs_frame_layout"
app:layout_constraintTop_toBottomOf="@id/tabs_frame_layout"
android:paddingBottom="10dp" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorSecondary"
android:stateListAnimator="@null"
android:theme="?attr/actionBarTheme"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<eu.kanade.tachiyomi.ui.base.CenteredToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
app:titleTextColor="?actionBarTintColor"
android:layout_height="?attr/actionBarSize"
app:collapseIcon="@drawable/ic_arrow_back_24dp"
android:background="?attr/colorSecondary">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/toolbar_title"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTint="?actionBarTintColor"
android:ellipsize="end"
android:layout_gravity="center"
android:maxLines="1"
android:textColor="?actionBarTintColor"
android:textSize="20sp"
tools:drawableEnd="@drawable/ic_arrow_drop_down_24dp"
tools:drawableStart="@drawable/ic_blank_24dp"
tools:text="Title Text" />
</eu.kanade.tachiyomi.ui.base.CenteredToolbar>
<FrameLayout
android:id="@+id/card_frame"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" >
<androidx.cardview.widget.CardView
android:id="@+id/card_view"
android:layout_width="match_parent"
android:layout_marginTop="4dp"
app:cardBackgroundColor="?colorPrimaryVariant"
android:layout_marginBottom="4dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="14dp"
app:cardCornerRadius="8dp"
android:layout_height="match_parent" >
<eu.kanade.tachiyomi.ui.base.FloatingToolbar
android:id="@+id/card_toolbar"
tools:title="Search Library..."
app:contentInsetStartWithNavigation="0dp"
app:contentInsetEndWithActions="0dp"
app:contentInsetStart="0dp"
app:contentInsetEnd="0dp"
android:background="@drawable/rect_ripple"
app:navigationIconTint="@color/text_color_secondary"
app:navigationIcon="@drawable/ic_search_24dp"
app:titleTextAppearance="@style/TextAppearance.MaterialComponents.Body1"
app:subtitleTextAppearance="@style/TextAppearance.MaterialComponents.Caption"
app:collapseIcon="@drawable/ic_arrow_back_24dp"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center"
android:orientation="horizontal">
<ImageView
android:id="@+id/card_incog_image"
app:tint="?actionBarTintColor"
android:layout_gravity="center|start"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="6dp"
android:layout_marginStart="0dp"
android:contentDescription="@string/incognito_mode"
android:src="@drawable/ic_incognito_circle_24dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="start|center"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/card_title"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTint="?actionBarTintColor"
android:ellipsize="end"
android:layout_gravity="center"
android:maxLines="1"
android:textColor="?actionBarTintColor"
android:textSize="20sp"
tools:drawableEnd="@drawable/ic_arrow_drop_down_24dp"
tools:drawableStart="@drawable/ic_blank_24dp"
tools:text="Title Text" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/card_subtitle"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:layout_gravity="start|center"
android:maxLines="1"
android:textColor="?actionBarTintColor"
android:textSize="12sp"
tools:text="Title Text" />
</LinearLayout>
</LinearLayout>
</eu.kanade.tachiyomi.ui.base.FloatingToolbar>
</androidx.cardview.widget.CardView>
</FrameLayout>
</com.google.android.material.appbar.AppBarLayout>
</androidx.constraintlayout.widget.ConstraintLayout>