Fix chapters sheet interaction

This commit is contained in:
Koitharu
2024-04-21 14:38:51 +03:00
parent fb716d300e
commit 19a3f14190
10 changed files with 65 additions and 35 deletions

View File

@@ -6,7 +6,12 @@ import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.onStart
import org.koitharu.kotatsu.core.util.ContinuationResumeRunnable
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
@@ -48,4 +53,14 @@ open class BaseListAdapter<T : ListModel> : AsyncListDifferDelegationAdapter<T>(
}
return null
}
fun observeItems(): Flow<List<T>> = callbackFlow {
val listListener = ListListener<T> { _, list ->
trySendBlocking(list)
}
addListListener(listListener)
awaitClose { removeListListener(listListener) }
}.onStart {
emit(items)
}
}

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.ui.list
import android.app.Notification.Action
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
@@ -80,8 +81,7 @@ class ListSelectionController(
}
fun onItemLongClick(id: Long): Boolean {
startActionMode()
return actionMode?.also {
return startActionMode()?.also {
decoration.setItemIsChecked(id, true)
notifySelectionChanged()
} != null
@@ -105,9 +105,9 @@ class ListSelectionController(
actionMode = null
}
private fun startActionMode() {
if (actionMode == null) {
actionMode = appCompatDelegate.startSupportActionMode(this)
private fun startActionMode(): ActionMode? {
return actionMode ?: appCompatDelegate.startSupportActionMode(this).also {
actionMode = it
}
}

View File

@@ -9,8 +9,8 @@ class Event<T>(
suspend fun consume(collector: FlowCollector<T>) {
if (!isConsumed) {
collector.emit(data)
isConsumed = true
collector.emit(data)
}
}

View File

@@ -1,5 +1,6 @@
package org.koitharu.kotatsu.core.util.ext
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
@@ -28,7 +29,7 @@ fun <T> Flow<T>.observe(owner: LifecycleOwner, minState: Lifecycle.State, collec
fun <T> Flow<Event<T>?>.observeEvent(owner: LifecycleOwner, collector: FlowCollector<T>) {
owner.lifecycleScope.launch {
owner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
owner.repeatOnLifecycle(Lifecycle.State.STARTED) {
collect {
it?.consume(collector)
}

View File

@@ -36,6 +36,7 @@ import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNot
import kotlinx.coroutines.flow.filterNotNull
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.bookmarks.domain.Bookmark
@@ -54,6 +55,7 @@ import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.Event
import org.koitharu.kotatsu.core.util.FileSize
import org.koitharu.kotatsu.core.util.ViewBadge
import org.koitharu.kotatsu.core.util.ext.crossfade
@@ -156,8 +158,12 @@ class DetailsActivity :
viewModel.details.filterNotNull().observe(this, ::onMangaUpdated)
viewModel.onMangaRemoved.observeEvent(this, ::onMangaRemoved)
viewModel.newChaptersCount.observe(this, ::onNewChaptersChanged)
viewModel.onError.observeEvent(this, DetailsErrorObserver(this, viewModel, exceptionResolver))
viewModel.onActionDone.observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null))
viewModel.onError
.filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) }
.observeEvent(this, DetailsErrorObserver(this, viewModel, exceptionResolver))
viewModel.onActionDone
.filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) }
.observeEvent(this, ReversibleActionObserver(viewBinding.scrollView, null))
combine(viewModel.historyInfo, viewModel.isLoading, ::Pair).observe(this) {
onHistoryChanged(it.first, it.second)
}
@@ -177,7 +183,9 @@ class DetailsActivity :
viewBinding.infoLayout.chipBranch.isVisible = it.size > 1
}
viewModel.chapters.observe(this, PrefetchObserver(this))
viewModel.onDownloadStarted.observeEvent(this, DownloadStartedObserver(viewBinding.scrollView))
viewModel.onDownloadStarted
.filterNot { ChaptersPagesSheet.isShown(supportFragmentManager) }
.observeEvent(this, DownloadStartedObserver(viewBinding.scrollView))
addMenuProvider(
DetailsMenuProvider(

View File

@@ -19,6 +19,7 @@ import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ShareHelper
import org.koitharu.kotatsu.details.ui.pager.ChaptersPagesSheet
import org.koitharu.kotatsu.download.ui.dialog.DownloadOption
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.scrobbling.common.ui.selector.ScrobblingSelectorSheet
@@ -134,6 +135,7 @@ class DetailsMenuProvider(
is DownloadOption.WholeManga -> null
is DownloadOption.SelectionHint -> {
viewModel.startChaptersSelection()
ChaptersPagesSheet.show(activity.supportFragmentManager, ChaptersPagesSheet.TAB_CHAPTERS)
return
}

View File

@@ -7,19 +7,24 @@ import androidx.appcompat.view.ActionMode
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.Lifecycle
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.ui.util.ActionModeListener
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
import org.koitharu.kotatsu.core.util.ext.menuView
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.recyclerView
import org.koitharu.kotatsu.core.util.ext.setTabsEnabled
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.ext.withArgs
import org.koitharu.kotatsu.databinding.SheetChaptersPagesBinding
import org.koitharu.kotatsu.details.ui.DetailsViewModel
import org.koitharu.kotatsu.download.ui.worker.DownloadStartedObserver
import javax.inject.Inject
@AndroidEntryPoint
@@ -39,13 +44,14 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
disableFitToContents()
val args = arguments ?: Bundle.EMPTY
val adapter = ChaptersPagesAdapter(this, args.getBoolean(ARG_SHOW_PAGES, settings.isPagesTabEnabled))
val defaultTab = args.getInt(ARG_TAB, settings.defaultDetailsTab)
val adapter = ChaptersPagesAdapter(this, settings.isPagesTabEnabled || defaultTab == TAB_PAGES)
binding.pager.recyclerView?.isNestedScrollingEnabled = false
binding.pager.offscreenPageLimit = adapter.itemCount
binding.pager.adapter = adapter
binding.pager.doOnPageChanged(::onPageChanged)
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
binding.pager.setCurrentItem(args.getInt(ARG_TAB, settings.defaultDetailsTab), false)
binding.pager.setCurrentItem(defaultTab, false)
binding.tabs.isVisible = adapter.itemCount > 1
val menuProvider = ChapterPagesMenuProvider(viewModel, this, binding.pager, settings)
@@ -53,16 +59,20 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
binding.toolbar.addMenuProvider(menuProvider)
actionModeDelegate.addListener(this, viewLifecycleOwner)
viewModel.onError.observeEvent(viewLifecycleOwner, SnackbarErrorObserver(binding.pager, this))
viewModel.onActionDone.observeEvent(viewLifecycleOwner, ReversibleActionObserver(binding.pager, null))
viewModel.onDownloadStarted.observeEvent(viewLifecycleOwner, DownloadStartedObserver(binding.pager))
}
override fun onActionModeStarted(mode: ActionMode) {
expandAndLock()
viewBinding?.toolbar?.menuView?.isEnabled = false
viewBinding?.toolbar?.menuView?.isVisible = false
}
override fun onActionModeFinished(mode: ActionMode) {
unlock()
viewBinding?.toolbar?.menuView?.isEnabled = true
viewBinding?.toolbar?.menuView?.isVisible = true
}
override fun expandAndLock() {
@@ -93,6 +103,8 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
const val TAB_PAGES = 1
const val TAB_BOOKMARKS = 2
private const val ARG_TAB = "tag"
@Deprecated("")
private const val ARG_SHOW_PAGES = "pages"
private const val TAG = "ChaptersPagesSheet"
@@ -100,17 +112,15 @@ class ChaptersPagesSheet : BaseAdaptiveSheet<SheetChaptersPagesBinding>(), Actio
ChaptersPagesSheet().showDistinct(fm, TAG)
}
fun show(fm: FragmentManager, showPagesTab: Boolean) {
fun show(fm: FragmentManager, defaultTab: Int) {
ChaptersPagesSheet().withArgs(1) {
putBoolean(ARG_SHOW_PAGES, showPagesTab)
}.showDistinct(fm, TAG)
}
fun show(fm: FragmentManager, showPagesTab: Boolean, defaultTab: Int) {
ChaptersPagesSheet().withArgs(2) {
putBoolean(ARG_SHOW_PAGES, showPagesTab)
putInt(ARG_TAB, defaultTab)
}.showDistinct(fm, TAG)
}
fun isShown(fm: FragmentManager): Boolean {
val sheet = fm.findFragmentByTag(TAG) as? ChaptersPagesSheet
return sheet != null && sheet.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)
}
}
}

View File

@@ -14,6 +14,7 @@ import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import org.koitharu.kotatsu.R
@@ -90,8 +91,11 @@ class ChaptersFragment :
viewModel.isChaptersEmpty.observe(viewLifecycleOwner) {
binding.textViewHolder.isVisible = it
}
viewModel.onSelectChapter.observeEvent(viewLifecycleOwner) {
selectionController?.onItemLongClick(it)
viewModel.onSelectChapter.observeEvent(viewLifecycleOwner) { chapterId ->
chaptersAdapter?.observeItems()?.firstOrNull { items ->
items.any { x -> x is ChapterListItem && x.chapter.id == chapterId }
}
selectionController?.onItemLongClick(chapterId)
}
}

View File

@@ -59,7 +59,7 @@ class ReaderBottomMenuProvider(
}
R.id.action_pages_thumbs -> {
ChaptersPagesSheet.show(activity.supportFragmentManager, true, ChaptersPagesSheet.TAB_PAGES)
ChaptersPagesSheet.show(activity.supportFragmentManager, ChaptersPagesSheet.TAB_PAGES)
true
}

View File

@@ -38,15 +38,5 @@ class ScrobblerMangaSelectionDecoration(context: Context) : MangaSelectionDecora
paint.color = strokeColor
paint.style = Paint.Style.STROKE
canvas.drawRoundRect(bounds, defaultRadius, defaultRadius, paint)
checkIcon?.run {
val offset = (bounds.height() - intrinsicHeight) / 2
setBounds(
(bounds.right - offset - intrinsicWidth).toInt(),
(bounds.top + offset).toInt(),
(bounds.right - offset).toInt(),
(bounds.top + offset + intrinsicHeight).toInt(),
)
draw(canvas)
}
}
}