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.*
|
||||
|
||||
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
|
||||
|
||||
@@ -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"
|
||||
|
||||
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