Convert the Chapter sheet into a dialog

This commit is contained in:
Jobobby04 2021-05-07 14:27:07 -04:00
parent cb2432cce9
commit aa6013b7ca
10 changed files with 168 additions and 257 deletions

View File

@ -306,12 +306,10 @@ dependencies {
testImplementation("com.ms-square:debugoverlay-no-op:$debugOverlayVersion") testImplementation("com.ms-square:debugoverlay-no-op:$debugOverlayVersion")
// RatingBar (SY) // RatingBar (SY)
implementation ("me.zhanghai.android.materialratingbar:library:1.4.0") implementation("me.zhanghai.android.materialratingbar:library:1.4.0")
// JsonReader for similar manga // JsonReader for similar manga
implementation("com.squareup.moshi:moshi:1.12.0") implementation("com.squareup.moshi:moshi:1.12.0")
implementation("com.mikepenz:fastadapter:5.4.1")
// SY <-- // SY <--
} }

View File

@ -26,7 +26,14 @@ sealed class Filter<T>(val name: String, var state: T) {
} }
// SY --> // SY -->
abstract class AutoComplete(name: String, val hint: String, val values: List<String>, val skipAutoFillTags: List<String> = emptyList(), val excludePrefix: String? = null, state: List<String>) : Filter<List<String>>(name, state) abstract class AutoComplete(
name: String,
val hint: String,
val values: List<String>,
val skipAutoFillTags: List<String> = emptyList(),
val excludePrefix: String? = null,
state: List<String>
) : Filter<List<String>>(name, state)
// SY <-- // SY <--
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {

View File

@ -48,7 +48,7 @@ import eu.kanade.tachiyomi.ui.manga.MangaController
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.AddToLibraryFirst
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Error
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success import eu.kanade.tachiyomi.ui.reader.ReaderPresenter.SetAsCoverResult.Success
import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterSheet import eu.kanade.tachiyomi.ui.reader.chapter.ReaderChapterDialog
import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader import eu.kanade.tachiyomi.ui.reader.loader.HttpPageLoader
import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter import eu.kanade.tachiyomi.ui.reader.model.ReaderChapter
import eu.kanade.tachiyomi.ui.reader.model.ReaderPage import eu.kanade.tachiyomi.ui.reader.model.ReaderPage
@ -144,8 +144,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
private val autoScrollFlow = MutableSharedFlow<Unit>() private val autoScrollFlow = MutableSharedFlow<Unit>()
private var autoScrollJob: Job? = null private var autoScrollJob: Job? = null
private val sourceManager: SourceManager by injectLazy() private val sourceManager: SourceManager by injectLazy()
private lateinit var chapterBottomSheet: ReaderChapterSheet
// SY <-- // SY <--
/** /**
@ -245,9 +243,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
super.onDestroy() super.onDestroy()
viewer?.destroy() viewer?.destroy()
viewer = null viewer = null
// SY -->
chapterBottomSheet.adapter = null
// SY <--
config = null config = null
menuToggleToast?.cancel() menuToggleToast?.cancel()
readingModeToast?.cancel() readingModeToast?.cancel()
@ -545,7 +540,7 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
setTooltip(R.string.chapters) setTooltip(R.string.chapters)
setOnClickListener { setOnClickListener {
chapterBottomSheet.show() ReaderChapterDialog(this@ReaderActivity)
} }
} }
@ -706,8 +701,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
} }
.launchIn(lifecycleScope) .launchIn(lifecycleScope)
chapterBottomSheet = ReaderChapterSheet(this)
updateBottomButtons() updateBottomButtons()
// <-- EH // <-- EH
@ -880,10 +873,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
) )
startActivity(intent) startActivity(intent)
} }
fun refreshSheetChapters() {
chapterBottomSheet.refreshList()
}
// SY <-- // SY <--
/** /**
@ -1067,12 +1056,6 @@ class ReaderActivity : BaseRxActivity<ReaderActivityBinding, ReaderPresenter>()
val newChapter = presenter.onPageSelected(page) val newChapter = presenter.onPageSelected(page)
val pages = page.chapter.pages ?: return val pages = page.chapter.pages ?: return
// SY -->
if (chapterBottomSheet.selectedChapterId != page.chapter.chapter.id) {
chapterBottomSheet.refreshList()
}
// SY <--
// Set bottom page number // Set bottom page number
binding.pageNumber.text = "${page.number}/${pages.size}" binding.pageNumber.text = "${page.number}/${pages.size}"
// binding.pageText.text = "${page.number}/${pages.size}" // binding.pageText.text = "${page.number}/${pages.size}"

View File

@ -477,11 +477,6 @@ class ReaderPresenter(
Timber.d("Setting ${selectedChapter.chapter.url} as active") Timber.d("Setting ${selectedChapter.chapter.url} as active")
onChapterChanged(currentChapters.currChapter) onChapterChanged(currentChapters.currChapter)
loadNewChapter(selectedChapter) loadNewChapter(selectedChapter)
// SY -->
Observable.just(selectedChapter).subscribeFirst({ view, _ ->
view.refreshSheetChapters()
})
// SY <--
} }
} }

View File

@ -0,0 +1,21 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.data.database.models.Chapter
class ReaderChapterAdapter(
dialog: OnBookmarkClickListener
) : FlexibleAdapter<ReaderChapterItem>(null, dialog, true) {
/**
* Listener for browse item clicks.
*/
val clickListener: OnBookmarkClickListener = dialog
/**
* Listener which should be called when user clicks the download icons.
*/
interface OnBookmarkClickListener {
fun bookmarkChapter(chapter: Chapter)
}
}

View File

@ -0,0 +1,78 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import androidx.recyclerview.widget.LinearLayoutManager
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.callbacks.onDismiss
import com.afollestad.materialdialogs.customview.customView
import eu.davidea.flexibleadapter.FlexibleAdapter
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.ReaderChaptersSheetBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.dpToPx
class ReaderChapterDialog(private val activity: ReaderActivity) : ReaderChapterAdapter.OnBookmarkClickListener {
private val binding = ReaderChaptersSheetBinding.inflate(activity.layoutInflater, null, false)
var presenter: ReaderPresenter = activity.presenter
var adapter: FlexibleAdapter<ReaderChapterItem>? = null
var dialog: MaterialDialog
init {
dialog = MaterialDialog(activity)
.title(R.string.chapters)
.customView(view = binding.root)
.show {
adapter = ReaderChapterAdapter(this@ReaderChapterDialog)
binding.chapterRecycler.adapter = adapter
adapter?.mItemClickListener = FlexibleAdapter.OnItemClickListener { _, position ->
val item = adapter?.getItem(position)
if (item != null && item.id != presenter.getCurrentChapter()?.chapter?.id) {
dismiss()
presenter.loadNewChapterFromSheet(item)
}
true
}
binding.chapterRecycler.layoutManager = LinearLayoutManager(context)
onDismiss {
destroy()
}
refreshList()
}
}
private fun refreshList() {
launchUI {
val chapters = with(presenter.getChapters(activity)) {
when (activity.presenter.manga?.sorting) {
Manga.CHAPTER_SORTING_SOURCE -> sortedBy { it.source_order }
Manga.CHAPTER_SORTING_NUMBER -> sortedByDescending { it.chapter_number }
Manga.CHAPTER_SORTING_UPLOAD_DATE -> sortedBy { it.date_upload }
else -> sortedBy { it.source_order }
}
}
adapter?.clear()
adapter?.updateDataSet(chapters)
(binding.chapterRecycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
adapter?.getGlobalPositionOf(chapters.find { it.isCurrent }) ?: 0,
(binding.chapterRecycler.height / 2).dpToPx
)
}
}
fun destroy() {
adapter = null
}
override fun bookmarkChapter(chapter: Chapter) {
presenter.toggleBookmark(chapter)
refreshList()
}
}

View File

@ -4,50 +4,68 @@ import android.content.Context
import android.graphics.Typeface import android.graphics.Typeface
import android.text.SpannableStringBuilder import android.text.SpannableStringBuilder
import android.view.View import android.view.View
import android.widget.FrameLayout import androidx.recyclerview.widget.RecyclerView
import android.widget.ImageView import eu.davidea.flexibleadapter.FlexibleAdapter
import android.widget.TextView import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
import com.mikepenz.fastadapter.FastAdapter import eu.davidea.flexibleadapter.items.IFlexible
import com.mikepenz.fastadapter.items.AbstractItem import eu.davidea.viewholders.FlexibleViewHolder
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.ReaderChapterItemBinding
import eu.kanade.tachiyomi.util.system.getResourceColor import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.view.setVectorCompat import eu.kanade.tachiyomi.util.view.setVectorCompat
import java.text.DateFormat import java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.util.Date import java.util.Date
class ReaderChapterItem(val chapter: Chapter, val manga: Manga, val isCurrent: Boolean, context: Context, val dateFormat: DateFormat, val decimalFormat: DecimalFormat) : class ReaderChapterItem(chapter: Chapter, val manga: Manga, val isCurrent: Boolean, context: Context, val dateFormat: DateFormat, val decimalFormat: DecimalFormat) :
AbstractItem<ReaderChapterItem.ViewHolder>(), AbstractFlexibleItem<ReaderChapterItem.ViewHolder>(),
Chapter by chapter { Chapter by chapter {
val readColor = context.getResourceColor(R.attr.colorOnSurface, 0.38f) val readColor = context.getResourceColor(R.attr.colorOnSurface, 0.38f)
val unreadColor = context.getResourceColor(R.attr.colorOnSurface) val unreadColor = context.getResourceColor(R.attr.colorOnSurface)
val bookmarkedColor = context.getResourceColor(R.attr.colorAccent) val bookmarkedColor = context.getResourceColor(R.attr.colorAccent)
/** defines the type defining this item. must be unique. preferably an id */ override fun getLayoutRes(): Int {
override val type: Int = R.id.reader_chapter_layout return R.layout.reader_chapter_item
/** defines the layout which will be used for this item in the list */
override val layoutRes: Int = R.layout.reader_chapter_item
override var identifier: Long = chapter.id!!
override fun getViewHolder(v: View): ViewHolder {
return ViewHolder(v)
} }
class ViewHolder(view: View) : FastAdapter.ViewHolder<ReaderChapterItem>(view) { override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): ViewHolder {
private var chapterTitle: TextView = view.findViewById(R.id.chapter_title) return ViewHolder(view, adapter as ReaderChapterAdapter)
private var chapterSubtitle: TextView = view.findViewById(R.id.chapter_scanlator) }
var bookmarkButton: FrameLayout = view.findViewById(R.id.bookmark_layout)
private var bookmarkImage: ImageView = view.findViewById(R.id.bookmark_image)
override fun bindView(item: ReaderChapterItem, payloads: List<Any>) { override fun bindViewHolder(
adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>,
holder: ViewHolder,
position: Int,
payloads: List<Any?>?
) {
holder.bind(this)
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as ReaderChapterItem
if (id != other.id) return false
return true
}
override fun hashCode(): Int {
return id.hashCode()
}
inner class ViewHolder(view: View, private val adapter: ReaderChapterAdapter) : FlexibleViewHolder(view, adapter) {
val binding = ReaderChapterItemBinding.bind(itemView)
fun bind(item: ReaderChapterItem) {
val manga = item.manga val manga = item.manga
chapterTitle.text = when (manga.displayMode) { binding.chapterTitle.text = when (manga.displayMode) {
Manga.CHAPTER_DISPLAY_NUMBER -> { Manga.CHAPTER_DISPLAY_NUMBER -> {
val number = item.decimalFormat.format(item.chapter_number.toDouble()) val number = item.decimalFormat.format(item.chapter_number.toDouble())
itemView.context.getString(R.string.display_mode_chapter, number) itemView.context.getString(R.string.display_mode_chapter, number)
@ -61,8 +79,8 @@ class ReaderChapterItem(val chapter: Chapter, val manga: Manga, val isCurrent: B
item.bookmark -> item.bookmarkedColor item.bookmark -> item.bookmarkedColor
else -> item.unreadColor else -> item.unreadColor
} }
chapterTitle.setTextColor(chapterColor) binding.chapterTitle.setTextColor(chapterColor)
chapterSubtitle.setTextColor(chapterColor) binding.chapterScanlator.setTextColor(chapterColor)
// bookmarkImage.isVisible = item.bookmark // bookmarkImage.isVisible = item.bookmark
@ -76,29 +94,27 @@ class ReaderChapterItem(val chapter: Chapter, val manga: Manga, val isCurrent: B
} }
if (descriptions.isNotEmpty()) { if (descriptions.isNotEmpty()) {
chapterSubtitle.text = descriptions.joinTo(SpannableStringBuilder(), "") binding.chapterScanlator.text = descriptions.joinTo(SpannableStringBuilder(), "")
} else { } else {
chapterSubtitle.text = "" binding.chapterScanlator.text = ""
} }
if (item.bookmark) { if (item.bookmark) {
bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_24dp, R.attr.colorAccent) binding.bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_24dp, R.attr.colorAccent)
} else { } else {
bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_border_24dp, R.attr.colorOnSurface) binding.bookmarkImage.setVectorCompat(R.drawable.ic_bookmark_border_24dp, R.attr.colorOnSurface)
} }
if (item.isCurrent) { if (item.isCurrent) {
chapterTitle.setTypeface(null, Typeface.BOLD_ITALIC) binding.chapterTitle.setTypeface(null, Typeface.BOLD_ITALIC)
chapterSubtitle.setTypeface(null, Typeface.BOLD_ITALIC) binding.chapterScanlator.setTypeface(null, Typeface.BOLD_ITALIC)
} else { } else {
chapterTitle.setTypeface(null, Typeface.NORMAL) binding.chapterTitle.setTypeface(null, Typeface.NORMAL)
chapterSubtitle.setTypeface(null, Typeface.NORMAL) binding.chapterScanlator.setTypeface(null, Typeface.NORMAL)
}
binding.bookmarkLayout.setOnClickListener {
adapter.clickListener.bookmarkChapter(item)
} }
}
override fun unbindView(item: ReaderChapterItem) {
chapterTitle.text = null
chapterSubtitle.text = null
} }
} }
} }

View File

@ -1,142 +0,0 @@
package eu.kanade.tachiyomi.ui.reader.chapter
import android.annotation.SuppressLint
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.mikepenz.fastadapter.FastAdapter
import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.fastadapter.listeners.ClickEventHook
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.databinding.ReaderChaptersSheetBinding
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
import eu.kanade.tachiyomi.ui.reader.ReaderPresenter
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.widget.SimpleSeekBarListener
import eu.kanade.tachiyomi.widget.sheet.BaseBottomSheetDialog
import exh.util.isExpanded
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import reactivecircus.flowbinding.android.view.clicks
/**
* Color filter sheet to toggle custom filter and brightness overlay.
*/
class ReaderChapterSheet(private val activity: ReaderActivity) : BaseBottomSheetDialog(activity) {
private var sheetBehavior: BottomSheetBehavior<*>? = null
private val binding = ReaderChaptersSheetBinding.inflate(activity.layoutInflater, null, false)
private var initialized = false
var presenter: ReaderPresenter
var adapter: FastAdapter<ReaderChapterItem>? = null
private val itemAdapter = ItemAdapter<ReaderChapterItem>()
var shouldCollapse = true
var selectedChapterId = -1L
init {
setContentView(binding.root)
sheetBehavior = BottomSheetBehavior.from(binding.root.parent as ViewGroup)
presenter = activity.presenter
adapter = FastAdapter.with(itemAdapter)
binding.chapterRecycler.adapter = adapter
adapter?.onClickListener = { _, _, item, _ ->
if (!sheetBehavior.isExpanded()) {
false
} else {
if (item.chapter.id != presenter.getCurrentChapter()?.chapter?.id) {
shouldCollapse = false
presenter.loadNewChapterFromSheet(item.chapter)
}
true
}
}
adapter?.addEventHook(
object : ClickEventHook<ReaderChapterItem>() {
override fun onBind(viewHolder: RecyclerView.ViewHolder): View? {
return if (viewHolder is ReaderChapterItem.ViewHolder) {
viewHolder.bookmarkButton
} else {
null
}
}
override fun onClick(
v: View,
position: Int,
fastAdapter: FastAdapter<ReaderChapterItem>,
item: ReaderChapterItem
) {
presenter.toggleBookmark(item.chapter)
refreshList()
}
}
)
binding.chapterRecycler.layoutManager = LinearLayoutManager(context)
// refreshList()
binding.webviewButton.clicks()
.onEach { activity.openMangaInBrowser() }
.launchIn(activity.lifecycleScope)
binding.pageSeekbar.setOnSeekBarChangeListener(
object : SimpleSeekBarListener() {
@SuppressLint("SetTextI18n")
override fun onProgressChanged(seekBar: SeekBar, value: Int, fromUser: Boolean) {
if (activity.viewer != null && fromUser) {
binding.pageText.text = "${value + 1}/${binding.pageSeekbar.max + 1}"
binding.pageSeekbar.progress = value
activity.moveToPageIndex(value)
}
}
}
)
}
override fun onStart() {
super.onStart()
sheetBehavior?.skipCollapsed = true
sheetBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
}
override fun show() {
if (!initialized) {
refreshList()
initialized = true
}
binding.pageText.text = activity.binding.pageNumber.text
binding.pageSeekbar.max = activity.binding.pageSeekbar.max
binding.pageSeekbar.progress = activity.binding.pageSeekbar.progress
super.show()
}
fun refreshList() {
launchUI {
val chapters = with(presenter.getChapters(context)) {
when (activity.presenter.manga?.sorting) {
Manga.CHAPTER_SORTING_SOURCE -> sortedBy { it.source_order }
Manga.CHAPTER_SORTING_NUMBER -> sortedByDescending { it.chapter_number }
Manga.CHAPTER_SORTING_UPLOAD_DATE -> sortedBy { it.date_upload }
else -> sortedBy { it.source_order }
}
}
selectedChapterId = chapters.find { it.isCurrent }?.chapter?.id ?: -1L
itemAdapter.clear()
itemAdapter.add(chapters)
(binding.chapterRecycler.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
adapter?.getPosition(presenter.getCurrentChapter()?.chapter?.id ?: 0L) ?: 0,
(binding.chapterRecycler.height / 2).dpToPx
)
}
}
}

View File

@ -58,6 +58,6 @@
android:layout_height="30dp" android:layout_height="30dp"
android:layout_gravity="center" android:layout_gravity="center"
android:src="@drawable/ic_bookmark_border_24dp" android:src="@drawable/ic_bookmark_border_24dp"
android:tint="?attr/colorOnSurface" /> app:tint="?attr/colorOnSurface" />
</FrameLayout> </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,59 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout 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" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="300dp" android:layout_height="match_parent"
android:layout_gravity="bottom"
android:orientation="vertical"> android:orientation="vertical">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/topbar_layout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorSurface"
android:clickable="true"
android:focusable="true">
<ImageButton
android:id="@+id/webview_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/action_open_in_web_view"
android:padding="@dimen/material_layout_keylines_screen_edge_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_public_24dp"
app:tint="?attr/colorOnBackground" />
<eu.kanade.tachiyomi.ui.reader.ReaderSeekBar
android:id="@+id/page_seekbar"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/page_text"
app:layout_constraintStart_toEndOf="@id/webview_button"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/page_text"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="?attr/colorOnBackground"
android:textSize="15sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="100 / 105" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/chapter_recycler" android:id="@+id/chapter_recycler"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="244dp" android:layout_height="match_parent"
android:clipToPadding="false" android:clipToPadding="false"
tools:listitem="@layout/reader_chapter_item" /> tools:listitem="@layout/reader_chapter_item" />