Imrpove new chapters sheet

This commit is contained in:
Koitharu
2024-04-03 11:23:53 +03:00
parent b27d5607ac
commit 8174d236f6
14 changed files with 378 additions and 89 deletions

View File

@@ -37,6 +37,7 @@ import org.koitharu.kotatsu.reader.ui.thumbnails.PageThumbnail
import javax.inject.Inject
import kotlin.math.roundToInt
@Deprecated("")
@AndroidEntryPoint
class BookmarksSheet :
BaseAdaptiveSheet<SheetPagesBinding>(),

View File

@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingFooter
import org.koitharu.kotatsu.parsers.util.SuspendLazy
import javax.inject.Inject
@Deprecated("")
@HiltViewModel
class BookmarksSheetViewModel @Inject constructor(
savedStateHandle: SavedStateHandle,

View File

@@ -54,6 +54,10 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
val onBackPressedDispatcher: OnBackPressedDispatcher
get() = requireComponentDialog().onBackPressedDispatcher
var isLocked = false
private set
private var lockCounter = 0
final override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -138,6 +142,10 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
}
protected fun setExpanded(isExpanded: Boolean, isLocked: Boolean) {
this.isLocked = isLocked
if (!isLocked) {
lockCounter = 0
}
val b = behavior ?: return
if (isExpanded) {
b.state = BottomSheetBehavior.STATE_EXPANDED
@@ -165,6 +173,20 @@ abstract class BaseAdaptiveSheet<B : ViewBinding> : AppCompatDialogFragment() {
}
}
@CallSuper
open fun expandAndLock() {
lockCounter++
setExpanded(isExpanded = true, isLocked = true)
}
@CallSuper
open fun unlock() {
lockCounter--
if (lockCounter <= 0) {
setExpanded(isExpanded, false)
}
}
fun requireViewBinding(): B = checkNotNull(viewBinding) {
"Fragment $this did not return a ViewBinding from onCreateView() or this was called before onCreateView()."
}

View File

@@ -25,7 +25,7 @@ fun ImageView.newImageRequest(lifecycleOwner: LifecycleOwner, data: Any?): Image
}
// disposeImageRequest()
return ImageRequest.Builder(context)
.data(data?.takeUnless { it == "" })
.data(data?.takeUnless { it == "" || it == 0 })
.lifecycle(lifecycleOwner)
.crossfade(context)
.target(this)

View File

@@ -0,0 +1,75 @@
package org.koitharu.kotatsu.details.ui
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.widget.SearchView
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import java.lang.ref.WeakReference
class ChaptersMenuProvider2(
private val viewModel: DetailsViewModel,
private val sheet: BaseAdaptiveSheet<*>,
) : OnBackPressedCallback(false), MenuProvider, SearchView.OnQueryTextListener, MenuItem.OnActionExpandListener {
private var searchItemRef: WeakReference<MenuItem>? = null
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_chapters, menu)
val searchMenuItem = menu.findItem(R.id.action_search)
searchMenuItem.setOnActionExpandListener(this)
val searchView = searchMenuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.setIconifiedByDefault(false)
searchView.queryHint = searchMenuItem.title
searchItemRef = WeakReference(searchMenuItem)
}
override fun onPrepareMenu(menu: Menu) {
menu.findItem(R.id.action_search)?.isVisible = viewModel.isChaptersEmpty.value == false
menu.findItem(R.id.action_reversed)?.isChecked = viewModel.isChaptersReversed.value == true
menu.findItem(R.id.action_grid_view)?.isChecked = viewModel.isChaptersInGridView.value == true
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.action_reversed -> {
viewModel.setChaptersReversed(!menuItem.isChecked)
true
}
R.id.action_grid_view -> {
viewModel.setChaptersInGridView(!menuItem.isChecked)
true
}
else -> false
}
override fun handleOnBackPressed() {
searchItemRef?.get()?.collapseActionView()
}
override fun onMenuItemActionExpand(item: MenuItem): Boolean {
sheet.expandAndLock()
isEnabled = true
return true
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
isEnabled = false
(item.actionView as? SearchView)?.setQuery("", false)
viewModel.performChapterSearch(null)
sheet.unlock()
return true
}
override fun onQueryTextSubmit(query: String?): Boolean = false
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.performChapterSearch(newText)
return true
}
}

View File

@@ -37,8 +37,6 @@ import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.filterNotNull
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.adapter.BookmarksAdapter
import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksSheet
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.model.iconResId
@@ -96,7 +94,6 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.util.ellipsize
import org.koitharu.kotatsu.reader.ui.ReaderActivity
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.reader.ui.thumbnails.PagesThumbnailsSheet
import org.koitharu.kotatsu.scrobbling.common.domain.model.ScrobblingInfo
@@ -147,7 +144,6 @@ class DetailsActivity2 :
viewBinding.infoLayout.chipAuthor.setOnClickListener(this)
viewBinding.imageViewCover.setOnClickListener(this)
viewBinding.buttonDescriptionMore.setOnClickListener(this)
viewBinding.buttonBookmarksMore.setOnClickListener(this)
viewBinding.buttonScrobblingMore.setOnClickListener(this)
viewBinding.buttonRelatedMore.setOnClickListener(this)
viewBinding.infoLayout.chipSource.setOnClickListener(this)
@@ -175,7 +171,6 @@ class DetailsActivity2 :
viewModel.onActionDone.observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null))
viewModel.historyInfo.observe(this, ::onHistoryChanged)
viewModel.isLoading.observe(this, ::onLoadingStateChanged)
viewModel.bookmarks.observe(this, ::onBookmarksChanged)
viewModel.scrobblingInfo.observe(this, ::onScrobblingInfoChanged)
viewModel.localSize.observe(this, ::onLocalSizeChanged)
viewModel.relatedManga.observe(this, ::onRelatedMangaChanged)
@@ -273,11 +268,6 @@ class DetailsActivity2 :
ScrobblingSelectorSheet.show(supportFragmentManager, manga, null)
}
R.id.button_bookmarks_more -> {
val manga = viewModel.manga.value ?: return
BookmarksSheet.show(supportFragmentManager, manga)
}
R.id.button_related_more -> {
val manga = viewModel.manga.value ?: return
startActivity(RelatedMangaActivity.newIntent(v.context, manga))
@@ -455,20 +445,6 @@ class DetailsActivity2 :
}
}
private fun onBookmarksChanged(bookmarks: List<Bookmark>) {
var adapter = viewBinding.recyclerViewBookmarks.adapter as? BookmarksAdapter
viewBinding.groupBookmarks.isGone = bookmarks.isEmpty()
if (adapter != null) {
adapter.items = bookmarks
} else {
adapter = BookmarksAdapter(coil, this, this)
adapter.items = bookmarks
viewBinding.recyclerViewBookmarks.adapter = adapter
val spacing = resources.getDimensionPixelOffset(R.dimen.bookmark_list_spacing)
viewBinding.recyclerViewBookmarks.addItemDecoration(SpacingItemDecoration(spacing))
}
}
private fun onScrobblingInfoChanged(scrobblings: List<ScrobblingInfo>) {
var adapter = viewBinding.recyclerViewScrobbling.adapter as? ScrollingInfoAdapter
viewBinding.groupScrobbling.isGone = scrobblings.isEmpty()

View File

@@ -5,14 +5,19 @@ import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.fragment.app.activityViewModels
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
import org.koitharu.kotatsu.core.util.ext.menuView
import org.koitharu.kotatsu.core.util.ext.recyclerView
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding
import org.koitharu.kotatsu.details.ui.ChaptersMenuProvider2
import org.koitharu.kotatsu.details.ui.DetailsViewModel
import javax.inject.Inject
@AndroidEntryPoint
@@ -21,6 +26,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
@Inject
lateinit var settings: AppSettings
private val viewModel by activityViewModels<DetailsViewModel>()
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetChaptersPagesBinding {
return SheetChaptersPagesBinding.inflate(inflater, container, false)
}
@@ -29,28 +36,48 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
super.onViewBindingCreated(binding, savedInstanceState)
val adapter = DetailsPagerAdapter2(this, settings)
binding.pager.recyclerView?.isNestedScrollingEnabled = false
binding.pager.offscreenPageLimit = 1
binding.pager.offscreenPageLimit = adapter.itemCount
binding.pager.adapter = adapter
binding.pager.doOnPageChanged(::onPageChanged)
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
binding.pager.setCurrentItem(settings.defaultDetailsTab, false)
binding.tabs.isVisible = adapter.itemCount > 1
val menuProvider = ChaptersMenuProvider2(viewModel, this)
onBackPressedDispatcher.addCallback(viewLifecycleOwner, menuProvider)
binding.toolbar.addMenuProvider(menuProvider)
actionModeDelegate.addListener(this, viewLifecycleOwner)
}
override fun onActionModeStarted(mode: ActionMode) {
setExpanded(true, true)
viewBinding?.run {
pager.isUserInputEnabled = false
tabs.setTabsEnabled(false)
}
expandAndLock()
viewBinding?.toolbar?.menuView?.isEnabled = false
}
override fun onActionModeFinished(mode: ActionMode) {
setExpanded(isExpanded, false)
unlock()
viewBinding?.toolbar?.menuView?.isEnabled = true
}
override fun expandAndLock() {
super.expandAndLock()
adjustLockState()
}
override fun unlock() {
super.unlock()
adjustLockState()
}
private fun adjustLockState() {
viewBinding?.run {
pager.isUserInputEnabled = true
tabs.setTabsEnabled(true)
pager.isUserInputEnabled = !isLocked
tabs.setTabsEnabled(!isLocked)
}
}
private fun onPageChanged(position: Int) {
viewBinding?.toolbar?.menuView?.isVisible = position == 0
}
}

View File

@@ -6,6 +6,7 @@ import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.details.ui.pager.bookmarks.MangaBookmarksFragment
import org.koitharu.kotatsu.details.ui.pager.chapters.ChaptersFragment
import org.koitharu.kotatsu.details.ui.pager.pages.PagesFragment
@@ -17,11 +18,12 @@ class DetailsPagerAdapter2(
val isPagesTabEnabled = settings.isPagesTabEnabled
override fun getItemCount(): Int = if (isPagesTabEnabled) 2 else 1
override fun getItemCount(): Int = if (isPagesTabEnabled) 3 else 2
override fun createFragment(position: Int): Fragment = when (position) {
0 -> ChaptersFragment()
1 -> PagesFragment()
1 -> if (isPagesTabEnabled) PagesFragment() else MangaBookmarksFragment()
2 -> MangaBookmarksFragment()
else -> throw IllegalArgumentException("Invalid position $position")
}
@@ -29,7 +31,8 @@ class DetailsPagerAdapter2(
tab.setText(
when (position) {
0 -> R.string.chapters
1 -> R.string.pages
1 -> if (isPagesTabEnabled) R.string.pages else R.string.bookmarks
2 -> R.string.bookmarks
else -> 0
},
)

View File

@@ -0,0 +1,133 @@
package org.koitharu.kotatsu.details.ui.pager.bookmarks
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.fragment.app.activityViewModels
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.GridLayoutManager
import coil.ImageLoader
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.ui.sheet.BookmarksAdapter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.FragmentMangaBookmarksBinding
import org.koitharu.kotatsu.details.ui.DetailsViewModel
import org.koitharu.kotatsu.list.ui.MangaListSpanResolver
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
import org.koitharu.kotatsu.list.ui.adapter.TypedListSpacingDecoration
import org.koitharu.kotatsu.reader.ui.ReaderActivity.IntentBuilder
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
import org.koitharu.kotatsu.reader.ui.thumbnails.OnPageSelectListener
import javax.inject.Inject
@AndroidEntryPoint
class MangaBookmarksFragment : BaseFragment<FragmentMangaBookmarksBinding>(),
OnListItemClickListener<Bookmark> {
private val activityViewModel by activityViewModels<DetailsViewModel>()
private val viewModel by viewModels<MangaBookmarksViewModel>()
@Inject
lateinit var coil: ImageLoader
@Inject
lateinit var settings: AppSettings
private var bookmarksAdapter: BookmarksAdapter? = null
private var spanResolver: MangaListSpanResolver? = null
private val spanSizeLookup = SpanSizeLookup()
private val listCommitCallback = Runnable {
spanSizeLookup.invalidateCache()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activityViewModel.manga.observe(this, viewModel)
}
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentMangaBookmarksBinding {
return FragmentMangaBookmarksBinding.inflate(inflater, container, false)
}
override fun onViewBindingCreated(binding: FragmentMangaBookmarksBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
spanResolver = MangaListSpanResolver(binding.root.resources)
bookmarksAdapter = BookmarksAdapter(
coil = coil,
lifecycleOwner = viewLifecycleOwner,
clickListener = this@MangaBookmarksFragment,
headerClickListener = null,
)
with(binding.recyclerView) {
addItemDecoration(TypedListSpacingDecoration(context, false))
adapter = bookmarksAdapter
addOnLayoutChangeListener(spanResolver)
spanResolver?.setGridSize(settings.gridSize / 100f, this)
(layoutManager as GridLayoutManager).spanSizeLookup = spanSizeLookup
}
viewModel.content.observe(viewLifecycleOwner, checkNotNull(bookmarksAdapter))
}
override fun onDestroyView() {
spanResolver = null
bookmarksAdapter = null
spanSizeLookup.invalidateCache()
super.onDestroyView()
}
override fun onPause() {
// required for BottomSheetBehavior
requireViewBinding().recyclerView.isNestedScrollingEnabled = false
super.onPause()
}
override fun onResume() {
requireViewBinding().recyclerView.isNestedScrollingEnabled = true
super.onResume()
}
override fun onWindowInsetsChanged(insets: Insets) = Unit
override fun onItemClick(item: Bookmark, view: View) {
val listener = (parentFragment as? OnPageSelectListener) ?: (activity as? OnPageSelectListener)
if (listener != null) {
listener.onPageSelected(ReaderPage(item.toMangaPage(), item.page, item.chapterId))
} else {
val intent = IntentBuilder(view.context)
.manga(activityViewModel.manga.value ?: return)
.bookmark(item)
.incognito(true)
.build()
startActivity(intent)
}
}
private inner class SpanSizeLookup : GridLayoutManager.SpanSizeLookup() {
init {
isSpanIndexCacheEnabled = true
isSpanGroupIndexCacheEnabled = true
}
override fun getSpanSize(position: Int): Int {
val total = (viewBinding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
return when (bookmarksAdapter?.getItemViewType(position)) {
ListItemType.PAGE_THUMB.ordinal -> 1
else -> total
}
}
fun invalidateCache() {
invalidateSpanGroupIndexCache()
invalidateSpanIndexCache()
}
}
}

View File

@@ -0,0 +1,68 @@
package org.koitharu.kotatsu.details.ui.pager.bookmarks
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
import org.koitharu.kotatsu.bookmarks.domain.BookmarksRepository
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.list.ui.model.EmptyState
import org.koitharu.kotatsu.list.ui.model.ListHeader
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.parsers.model.Manga
import javax.inject.Inject
@HiltViewModel
class MangaBookmarksViewModel @Inject constructor(
bookmarksRepository: BookmarksRepository,
) : BaseViewModel(), FlowCollector<Manga?> {
private val manga = MutableStateFlow<Manga?>(null)
val content: StateFlow<List<ListModel>> = manga.filterNotNull().flatMapLatest { m ->
bookmarksRepository.observeBookmarks(m)
.map { mapList(m, it) }
}.withErrorHandling()
.filterNotNull()
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, listOf(LoadingState))
override suspend fun emit(value: Manga?) {
manga.value = value
}
private suspend fun mapList(manga: Manga, bookmarks: List<Bookmark>): List<ListModel>? {
val chapters = manga.chapters ?: return null
val bookmarksMap = bookmarks.groupBy { it.chapterId }
val result = ArrayList<ListModel>(bookmarks.size + bookmarksMap.size)
for (chapter in chapters) {
val b = bookmarksMap[chapter.id]
if (b.isNullOrEmpty()) {
continue
}
result += ListHeader(chapter.name)
result.addAll(b)
}
if (result.isEmpty()) {
result.add(
EmptyState(
icon = 0,
textPrimary = R.string.no_bookmarks_yet,
textSecondary = R.string.no_bookmarks_summary,
actionStringRes = 0,
),
)
}
return result
}
}

View File

@@ -252,48 +252,6 @@
tools:ignore="UnusedAttribute"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/textView_bookmarks_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:padding="@dimen/grid_spacing"
android:singleLine="true"
android:text="@string/bookmarks"
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
app:layout_constraintEnd_toStartOf="@id/button_bookmarks_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_description" />
<Button
android:id="@+id/button_bookmarks_more"
style="@style/Widget.Kotatsu.Button.More"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="@string/show_all"
app:layout_constraintBaseline_toBaselineOf="@id/textView_bookmarks_title"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView_bookmarks"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="6dp"
android:clipToPadding="false"
android:nestedScrollingEnabled="false"
android:orientation="horizontal"
android:paddingStart="12dp"
android:paddingEnd="12dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView_bookmarks_title"
tools:listitem="@layout/item_bookmark" />
<TextView
android:id="@+id/textView_scrobbling_title"
android:layout_width="0dp"
@@ -308,7 +266,7 @@
android:textAppearance="@style/TextAppearance.Kotatsu.SectionHeader"
app:layout_constraintEnd_toStartOf="@id/button_scrobbling_more"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/recyclerView_bookmarks" />
app:layout_constraintTop_toBottomOf="@id/textView_description" />
<Button
android:id="@+id/button_scrobbling_more"

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<org.koitharu.kotatsu.core.ui.list.fastscroll.FastScrollRecyclerView
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/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:orientation="vertical"
android:scrollIndicators="top"
app:bubbleSize="small"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="3"
tools:listitem="@layout/item_bookmark" />

View File

@@ -12,13 +12,23 @@
android:layout_height="wrap_content"
app:title="@string/chapters" />
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.Material3.TabLayout.Secondary"
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@null"
app:tabUnboundedRipple="false" />
android:layout_height="wrap_content">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
style="@style/Widget.Material3.TabLayout.Secondary"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:background="@null"
app:tabGravity="start"
app:tabMode="scrollable"
app:tabUnboundedRipple="false" />
</com.google.android.material.appbar.MaterialToolbar>
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"