Fix chapters sheet interaction
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@ class Event<T>(
|
||||
|
||||
suspend fun consume(collector: FlowCollector<T>) {
|
||||
if (!isConsumed) {
|
||||
collector.emit(data)
|
||||
isConsumed = true
|
||||
collector.emit(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user