Replace chapters dialog with bottom sheet

This commit is contained in:
Koitharu
2022-03-04 20:10:29 +02:00
parent eb7e255430
commit 5158f4bd89
5 changed files with 164 additions and 102 deletions

View File

@@ -0,0 +1,125 @@
package org.koitharu.kotatsu.reader.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.BaseBottomSheet
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.SheetChaptersBinding
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.utils.ext.withArgs
class ChaptersBottomSheet : BaseBottomSheet<SheetChaptersBinding>(), OnListItemClickListener<ChapterListItem> {
override fun onInflateView(inflater: LayoutInflater, container: ViewGroup?): SheetChaptersBinding {
return SheetChaptersBinding.inflate(inflater, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.setNavigationOnClickListener { dismiss() }
if (!resources.getBoolean(R.bool.is_tablet)) {
binding.toolbar.navigationIcon = null
}
binding.recyclerView.addItemDecoration(
MaterialDividerItemDecoration(view.context, RecyclerView.VERTICAL)
)
val chapters = arguments?.getParcelableArrayList<MangaChapter>(ARG_CHAPTERS)
if (chapters.isNullOrEmpty()) {
dismissAllowingStateLoss()
return
}
val currentId = requireArguments().getLong(ARG_CURRENT_ID, 0L)
val currentPosition = chapters.indexOfFirst { it.id == currentId }
val dateFormat = get<AppSettings>().getDateFormat()
val items = chapters.mapIndexed { index, chapter ->
chapter.toListItem(
isCurrent = index == currentPosition,
isUnread = index > currentPosition,
isNew = false,
isMissing = false,
isDownloaded = false,
dateFormat = dateFormat,
)
}
binding.recyclerView.adapter = ChaptersAdapter(this).also { adapter ->
if (currentPosition >= 0) {
val targetPosition = (currentPosition - 1).coerceAtLeast(0)
adapter.setItems(items, Scroller(binding.recyclerView, targetPosition))
} else {
adapter.items = items
}
}
}
override fun onCreateDialog(savedInstanceState: Bundle?) = super.onCreateDialog(savedInstanceState).also {
val behavior = (it as? BottomSheetDialog)?.behavior ?: return@also
behavior.addBottomSheetCallback(
object : BottomSheetBehavior.BottomSheetCallback() {
override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == BottomSheetBehavior.STATE_EXPANDED) {
binding.toolbar.setNavigationIcon(R.drawable.ic_cross)
} else {
binding.toolbar.navigationIcon = null
}
}
}
)
}
override fun onItemClick(item: ChapterListItem, view: View) {
((parentFragment as? OnChapterChangeListener) ?: (activity as? OnChapterChangeListener))?.let {
dismiss()
it.onChapterChanged(item.chapter)
}
}
fun interface OnChapterChangeListener {
fun onChapterChanged(chapter: MangaChapter)
}
private class Scroller(private val recyclerView: RecyclerView, private val position: Int) : Runnable {
override fun run() {
val offset = recyclerView.resources.getDimensionPixelSize(R.dimen.chapter_list_item_height) / 2
(recyclerView.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(position, offset)
}
}
companion object {
private const val ARG_CHAPTERS = "chapters"
private const val ARG_CURRENT_ID = "current_id"
private const val TAG = "ChaptersBottomSheet"
fun show(
fm: FragmentManager,
chapters: List<MangaChapter>,
currentId: Long,
) = ChaptersBottomSheet().withArgs(2) {
putParcelableArrayList(ARG_CHAPTERS, chapters.asArrayList())
putLong(ARG_CURRENT_ID, currentId)
}.show(fm, TAG)
private fun <T> List<T>.asArrayList(): ArrayList<T> {
return this as? ArrayList<T> ?: ArrayList(this)
}
}
}

View File

@@ -1,99 +0,0 @@
package org.koitharu.kotatsu.reader.ui
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.divider.MaterialDividerItemDecoration
import org.koin.android.ext.android.get
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.AlertDialogFragment
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.MangaChapter
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.databinding.DialogChaptersBinding
import org.koitharu.kotatsu.details.ui.adapter.ChaptersAdapter
import org.koitharu.kotatsu.details.ui.model.ChapterListItem
import org.koitharu.kotatsu.details.ui.model.toListItem
import org.koitharu.kotatsu.utils.ext.withArgs
class ChaptersDialog : AlertDialogFragment<DialogChaptersBinding>(),
OnListItemClickListener<ChapterListItem> {
override fun onInflateView(
inflater: LayoutInflater,
container: ViewGroup?,
) = DialogChaptersBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: MaterialAlertDialogBuilder) {
builder.setTitle(R.string.chapters)
.setNegativeButton(R.string.close, null)
.setCancelable(true)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.recyclerViewChapters.addItemDecoration(
MaterialDividerItemDecoration(view.context, RecyclerView.VERTICAL)
)
val chapters = arguments?.getParcelableArrayList<MangaChapter>(ARG_CHAPTERS)
if (chapters == null) {
dismissAllowingStateLoss()
return
}
val currentId = arguments?.getLong(ARG_CURRENT_ID, 0L) ?: 0L
val currentPosition = chapters.indexOfFirst { it.id == currentId }
val dateFormat = get<AppSettings>().getDateFormat()
binding.recyclerViewChapters.adapter = ChaptersAdapter(this).apply {
setItems(chapters.mapIndexed { index, chapter ->
chapter.toListItem(
isCurrent = index == currentPosition,
isUnread = index > currentPosition,
isNew = false,
isMissing = false,
isDownloaded = false,
dateFormat = dateFormat,
)
}) {
if (currentPosition >= 0) {
with(binding.recyclerViewChapters) {
(layoutManager as LinearLayoutManager).scrollToPositionWithOffset(
currentPosition,
height / 3
)
}
}
}
}
}
override fun onItemClick(item: ChapterListItem, view: View) {
((parentFragment as? OnChapterChangeListener)
?: (activity as? OnChapterChangeListener))?.let {
dismiss()
it.onChapterChanged(item.chapter)
}
}
fun interface OnChapterChangeListener {
fun onChapterChanged(chapter: MangaChapter)
}
companion object {
private const val TAG = "ChaptersDialog"
private const val ARG_CHAPTERS = "chapters"
private const val ARG_CURRENT_ID = "current_id"
fun show(fm: FragmentManager, chapters: List<MangaChapter>, currentId: Long = 0L) =
ChaptersDialog().withArgs(2) {
putParcelableArrayList(ARG_CHAPTERS, ArrayList(chapters))
putLong(ARG_CURRENT_ID, currentId)
}.show(fm, TAG)
}
}

View File

@@ -51,7 +51,7 @@ import org.koitharu.kotatsu.utils.anim.Motion
import org.koitharu.kotatsu.utils.ext.*
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
ChaptersDialog.OnChapterChangeListener,
ChaptersBottomSheet.OnChapterChangeListener,
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
@@ -152,7 +152,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this))
}
R.id.action_chapters -> {
ChaptersDialog.show(
ChaptersBottomSheet.show(
supportFragmentManager,
viewModel.manga?.chapters.orEmpty(),
viewModel.getCurrentState()?.chapterId ?: 0L

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="@dimen/chapter_list_item_height"
@@ -54,7 +55,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="12dp"
android:src="@drawable/ic_new" />
android:src="@drawable/ic_new"
app:tint="?colorError" />
<ImageView
android:id="@+id/imageView_downloaded"

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="@drawable/ic_cross"
app:title="@string/chapters" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:orientation="vertical"
app:fastScrollEnabled="true"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/item_chapter" />
</LinearLayout>