Replace chapters dialog with bottom sheet
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,7 +51,7 @@ import org.koitharu.kotatsu.utils.anim.Motion
|
|||||||
import org.koitharu.kotatsu.utils.ext.*
|
import org.koitharu.kotatsu.utils.ext.*
|
||||||
|
|
||||||
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
||||||
ChaptersDialog.OnChapterChangeListener,
|
ChaptersBottomSheet.OnChapterChangeListener,
|
||||||
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
|
GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback,
|
||||||
ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
|
ActivityResultCallback<Boolean>, ReaderControlDelegate.OnInteractionListener {
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ class ReaderActivity : BaseFullscreenActivity<ActivityReaderBinding>(),
|
|||||||
startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this))
|
startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this))
|
||||||
}
|
}
|
||||||
R.id.action_chapters -> {
|
R.id.action_chapters -> {
|
||||||
ChaptersDialog.show(
|
ChaptersBottomSheet.show(
|
||||||
supportFragmentManager,
|
supportFragmentManager,
|
||||||
viewModel.manga?.chapters.orEmpty(),
|
viewModel.manga?.chapters.orEmpty(),
|
||||||
viewModel.getCurrentState()?.chapterId ?: 0L
|
viewModel.getCurrentState()?.chapterId ?: 0L
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
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="@dimen/chapter_list_item_height"
|
android:layout_height="@dimen/chapter_list_item_height"
|
||||||
@@ -54,7 +55,8 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="12dp"
|
android:layout_marginEnd="12dp"
|
||||||
android:src="@drawable/ic_new" />
|
android:src="@drawable/ic_new"
|
||||||
|
app:tint="?colorError" />
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/imageView_downloaded"
|
android:id="@+id/imageView_downloaded"
|
||||||
|
|||||||
34
app/src/main/res/layout/sheet_chapters.xml
Normal file
34
app/src/main/res/layout/sheet_chapters.xml
Normal 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>
|
||||||
Reference in New Issue
Block a user