From 5158f4bd89b844a994d43ab4a75d872edcaf0f93 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Fri, 4 Mar 2022 20:10:29 +0200 Subject: [PATCH] Replace chapters dialog with bottom sheet --- .../kotatsu/reader/ui/ChaptersBottomSheet.kt | 125 ++++++++++++++++++ .../kotatsu/reader/ui/ChaptersDialog.kt | 99 -------------- .../kotatsu/reader/ui/ReaderActivity.kt | 4 +- app/src/main/res/layout/item_chapter.xml | 4 +- app/src/main/res/layout/sheet_chapters.xml | 34 +++++ 5 files changed, 164 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt delete mode 100644 app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersDialog.kt create mode 100644 app/src/main/res/layout/sheet_chapters.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt new file mode 100644 index 000000000..1ec6ffa7a --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersBottomSheet.kt @@ -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(), OnListItemClickListener { + + 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(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().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, + currentId: Long, + ) = ChaptersBottomSheet().withArgs(2) { + putParcelableArrayList(ARG_CHAPTERS, chapters.asArrayList()) + putLong(ARG_CURRENT_ID, currentId) + }.show(fm, TAG) + + private fun List.asArrayList(): ArrayList { + return this as? ArrayList ?: ArrayList(this) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersDialog.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersDialog.kt deleted file mode 100644 index f091de3fd..000000000 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ChaptersDialog.kt +++ /dev/null @@ -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(), - OnListItemClickListener { - - 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(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().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, currentId: Long = 0L) = - ChaptersDialog().withArgs(2) { - putParcelableArrayList(ARG_CHAPTERS, ArrayList(chapters)) - putLong(ARG_CURRENT_ID, currentId) - }.show(fm, TAG) - } -} diff --git a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt index e4ee21e84..0f74db220 100644 --- a/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/reader/ui/ReaderActivity.kt @@ -51,7 +51,7 @@ import org.koitharu.kotatsu.utils.anim.Motion import org.koitharu.kotatsu.utils.ext.* class ReaderActivity : BaseFullscreenActivity(), - ChaptersDialog.OnChapterChangeListener, + ChaptersBottomSheet.OnChapterChangeListener, GridTouchHelper.OnGridTouchListener, OnPageSelectListener, ReaderConfigDialog.Callback, ActivityResultCallback, ReaderControlDelegate.OnInteractionListener { @@ -152,7 +152,7 @@ class ReaderActivity : BaseFullscreenActivity(), startActivity(SimpleSettingsActivity.newReaderSettingsIntent(this)) } R.id.action_chapters -> { - ChaptersDialog.show( + ChaptersBottomSheet.show( supportFragmentManager, viewModel.manga?.chapters.orEmpty(), viewModel.getCurrentState()?.chapterId ?: 0L diff --git a/app/src/main/res/layout/item_chapter.xml b/app/src/main/res/layout/item_chapter.xml index fda5fe0f2..30f631e10 100644 --- a/app/src/main/res/layout/item_chapter.xml +++ b/app/src/main/res/layout/item_chapter.xml @@ -1,6 +1,7 @@ + android:src="@drawable/ic_new" + app:tint="?colorError" /> + + + + + + + + + + + \ No newline at end of file