Readers refactor
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import androidx.viewpager2.widget.ViewPager2.PageTransformer
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderAnimation
|
||||
import org.koitharu.kotatsu.core.ui.list.lifecycle.PagerLifecycleDispatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentViewHolder
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.resetTransformations
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderPagerBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.NoAnimPageTransformer
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageAnimTransformer
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerEventSupplier
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagesAdapter
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
|
||||
@AndroidEntryPoint
|
||||
abstract class BasePagerReaderFragment : BaseReaderFragment<FragmentReaderPagerBinding>(),
|
||||
View.OnGenericMotionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
@Inject
|
||||
lateinit var pageLoader: PageLoader
|
||||
|
||||
private var pagerLifecycleDispatcher: PagerLifecycleDispatcher? = null
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
) = FragmentReaderPagerBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onViewBindingCreated(
|
||||
binding: FragmentReaderPagerBinding,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
with(binding.pager) {
|
||||
onInitPager(this)
|
||||
doOnPageChanged(::notifyPageChanged)
|
||||
setOnGenericMotionListener(this@BasePagerReaderFragment)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
recyclerView?.defaultFocusHighlightEnabled = false
|
||||
}
|
||||
PagerEventSupplier(this).attach()
|
||||
pagerLifecycleDispatcher = PagerLifecycleDispatcher(this).also {
|
||||
registerOnPageChangeCallback(it)
|
||||
}
|
||||
adapter = readerAdapter
|
||||
}
|
||||
|
||||
viewModel.pageAnimation.observe(viewLifecycleOwner) {
|
||||
val transformer = when (it) {
|
||||
ReaderAnimation.NONE -> NoAnimPageTransformer()
|
||||
ReaderAnimation.DEFAULT -> null
|
||||
ReaderAnimation.ADVANCED -> onCreateAdvancedTransformer()
|
||||
}
|
||||
binding.pager.setPageTransformer(transformer)
|
||||
if (transformer == null) {
|
||||
binding.pager.recyclerView?.children?.forEach { view ->
|
||||
view.resetTransformations()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerLifecycleDispatcher = null
|
||||
requireViewBinding().pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onZoomIn() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomIn()
|
||||
}
|
||||
|
||||
override fun onZoomOut() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomOut()
|
||||
}
|
||||
|
||||
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
|
||||
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
|
||||
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
|
||||
if (!withCtrl) {
|
||||
switchPageBy(-axisValue.sign.toInt())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
|
||||
val items = launch {
|
||||
requireAdapter().setItems(pages)
|
||||
yield()
|
||||
pagerLifecycleDispatcher?.invalidate()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = pages.indexOfFirst {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.join()
|
||||
if (position != -1) {
|
||||
requireViewBinding().pager.setCurrentItem(position, false)
|
||||
notifyPageChanged(position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.join()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateAdapter(): BaseReaderAdapter<*> = PagesAdapter(
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
|
||||
override fun switchPageBy(delta: Int) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(currentItem + delta, isAnimationEnabled())
|
||||
}
|
||||
}
|
||||
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(
|
||||
position,
|
||||
smooth && isAnimationEnabled() && (currentItem - position).absoluteValue < SMOOTH_SCROLL_LIMIT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentState(): ReaderState? = viewBinding?.run {
|
||||
val adapter = pager.adapter as? BaseReaderAdapter<*>
|
||||
val page = adapter?.getItemOrNull(pager.currentItem) ?: return@run null
|
||||
ReaderState(
|
||||
chapterId = page.chapterId,
|
||||
page = page.index,
|
||||
scroll = 0,
|
||||
)
|
||||
}
|
||||
|
||||
protected open fun onCreateAdvancedTransformer(): PageTransformer = PageAnimTransformer()
|
||||
|
||||
protected open fun onInitPager(pager: ViewPager2) {
|
||||
pager.offscreenPageLimit = 2
|
||||
}
|
||||
|
||||
protected open fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(page)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val SMOOTH_SCROLL_LIMIT = 3
|
||||
}
|
||||
}
|
||||
@@ -1,109 +1,15 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager.reversed
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderAnimation
|
||||
import org.koitharu.kotatsu.core.ui.list.lifecycle.PagerLifecycleDispatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentViewHolder
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.resetTransformations
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BasePagerReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.NoAnimPageTransformer
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerEventSupplier
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerReaderFragment
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>(),
|
||||
View.OnGenericMotionListener {
|
||||
class ReversedReaderFragment : BasePagerReaderFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
@Inject
|
||||
lateinit var pageLoader: PageLoader
|
||||
|
||||
private var pagerLifecycleDispatcher: PagerLifecycleDispatcher? = null
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
) = FragmentReaderStandardBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onViewBindingCreated(binding: FragmentReaderStandardBinding, savedInstanceState: Bundle?) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
with(binding.pager) {
|
||||
adapter = readerAdapter
|
||||
offscreenPageLimit = 2
|
||||
doOnPageChanged(::notifyPageChanged)
|
||||
setOnGenericMotionListener(this@ReversedReaderFragment)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
recyclerView?.defaultFocusHighlightEnabled = false
|
||||
}
|
||||
PagerEventSupplier(this).attach()
|
||||
pagerLifecycleDispatcher = PagerLifecycleDispatcher(this).also {
|
||||
registerOnPageChangeCallback(it)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.pageAnimation.observe(viewLifecycleOwner) {
|
||||
val transformer = when (it) {
|
||||
ReaderAnimation.NONE -> NoAnimPageTransformer()
|
||||
ReaderAnimation.DEFAULT -> null
|
||||
ReaderAnimation.ADVANCED -> ReversedPageAnimTransformer()
|
||||
}
|
||||
binding.pager.setPageTransformer(transformer)
|
||||
if (transformer == null) {
|
||||
binding.pager.recyclerView?.children?.forEach { v ->
|
||||
v.resetTransformations()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerLifecycleDispatcher = null
|
||||
requireViewBinding().pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
|
||||
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
|
||||
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
|
||||
if (!withCtrl) {
|
||||
switchPageBy(-axisValue.sign.toInt())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
override fun onCreateAdvancedTransformer(): ViewPager2.PageTransformer = ReversedPageAnimTransformer()
|
||||
|
||||
override fun onCreateAdapter() = ReversedPagesAdapter(
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
@@ -113,65 +19,19 @@ class ReversedReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
|
||||
override fun onZoomIn() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomIn()
|
||||
}
|
||||
|
||||
override fun onZoomOut() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomOut()
|
||||
}
|
||||
|
||||
|
||||
override fun switchPageBy(delta: Int) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(currentItem - delta, isAnimationEnabled())
|
||||
}
|
||||
super.switchPageBy(-delta)
|
||||
}
|
||||
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(
|
||||
reversed(position),
|
||||
smooth && isAnimationEnabled() && (currentItem - position).absoluteValue < PagerReaderFragment.SMOOTH_SCROLL_LIMIT,
|
||||
)
|
||||
}
|
||||
super.switchPageTo(reversed(position), smooth)
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) = coroutineScope {
|
||||
val reversedPages = pages.asReversed()
|
||||
val items = launch {
|
||||
requireAdapter().setItems(reversedPages)
|
||||
yield()
|
||||
pagerLifecycleDispatcher?.invalidate()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = reversedPages.indexOfLast {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.join()
|
||||
if (position != -1) {
|
||||
requireViewBinding().pager.setCurrentItem(position, false)
|
||||
notifyPageChanged(position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.join()
|
||||
}
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) {
|
||||
super.onPagesChanged(pages.reversed(), pendingState)
|
||||
}
|
||||
|
||||
override fun getCurrentState(): ReaderState? = viewBinding?.run {
|
||||
val adapter = pager.adapter as? BaseReaderAdapter<*>
|
||||
val page = adapter?.getItemOrNull(pager.currentItem) ?: return@run null
|
||||
ReaderState(
|
||||
chapterId = page.chapterId,
|
||||
page = page.index,
|
||||
scroll = 0,
|
||||
)
|
||||
}
|
||||
|
||||
private fun notifyPageChanged(page: Int) {
|
||||
override fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(reversed(page))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,180 +1,7 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager.standard
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderAnimation
|
||||
import org.koitharu.kotatsu.core.ui.list.lifecycle.PagerLifecycleDispatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentViewHolder
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.resetTransformations
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BasePagerReaderFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PagerReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>(),
|
||||
View.OnGenericMotionListener {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
@Inject
|
||||
lateinit var pageLoader: PageLoader
|
||||
|
||||
private var pagerLifecycleDispatcher: PagerLifecycleDispatcher? = null
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
) = FragmentReaderStandardBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onViewBindingCreated(
|
||||
binding: FragmentReaderStandardBinding,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
with(binding.pager) {
|
||||
adapter = readerAdapter
|
||||
offscreenPageLimit = 2
|
||||
doOnPageChanged(::notifyPageChanged)
|
||||
setOnGenericMotionListener(this@PagerReaderFragment)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
recyclerView?.defaultFocusHighlightEnabled = false
|
||||
}
|
||||
PagerEventSupplier(this).attach()
|
||||
pagerLifecycleDispatcher = PagerLifecycleDispatcher(this).also {
|
||||
registerOnPageChangeCallback(it)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.pageAnimation.observe(viewLifecycleOwner) {
|
||||
val transformer = when (it) {
|
||||
ReaderAnimation.NONE -> NoAnimPageTransformer()
|
||||
ReaderAnimation.DEFAULT -> null
|
||||
ReaderAnimation.ADVANCED -> PageAnimTransformer()
|
||||
}
|
||||
binding.pager.setPageTransformer(transformer)
|
||||
if (transformer == null) {
|
||||
binding.pager.recyclerView?.children?.forEach { view ->
|
||||
view.resetTransformations()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerLifecycleDispatcher = null
|
||||
requireViewBinding().pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onZoomIn() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomIn()
|
||||
}
|
||||
|
||||
override fun onZoomOut() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomOut()
|
||||
}
|
||||
|
||||
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
|
||||
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
|
||||
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
|
||||
if (!withCtrl) {
|
||||
switchPageBy(-axisValue.sign.toInt())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) =
|
||||
coroutineScope {
|
||||
val items = launch {
|
||||
requireAdapter().setItems(pages)
|
||||
yield()
|
||||
pagerLifecycleDispatcher?.invalidate()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = pages.indexOfFirst {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.join()
|
||||
if (position != -1) {
|
||||
requireViewBinding().pager.setCurrentItem(position, false)
|
||||
notifyPageChanged(position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.join()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateAdapter() = PagesAdapter(
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
|
||||
override fun switchPageBy(delta: Int) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(currentItem + delta, isAnimationEnabled())
|
||||
}
|
||||
}
|
||||
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(
|
||||
position,
|
||||
smooth && isAnimationEnabled() && (currentItem - position).absoluteValue < SMOOTH_SCROLL_LIMIT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentState(): ReaderState? = viewBinding?.run {
|
||||
val adapter = pager.adapter as? BaseReaderAdapter<*>
|
||||
val page = adapter?.getItemOrNull(pager.currentItem) ?: return@run null
|
||||
ReaderState(
|
||||
chapterId = page.chapterId,
|
||||
page = page.index,
|
||||
scroll = 0,
|
||||
)
|
||||
}
|
||||
|
||||
private fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(page)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val SMOOTH_SCROLL_LIMIT = 3
|
||||
}
|
||||
}
|
||||
class PagerReaderFragment : BasePagerReaderFragment()
|
||||
|
||||
@@ -1,186 +1,16 @@
|
||||
package org.koitharu.kotatsu.reader.ui.pager.vertical
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.children
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.prefs.ReaderAnimation
|
||||
import org.koitharu.kotatsu.core.ui.list.lifecycle.PagerLifecycleDispatcher
|
||||
import org.koitharu.kotatsu.core.util.ext.doOnPageChanged
|
||||
import org.koitharu.kotatsu.core.util.ext.findCurrentViewHolder
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.recyclerView
|
||||
import org.koitharu.kotatsu.core.util.ext.resetTransformations
|
||||
import org.koitharu.kotatsu.databinding.FragmentReaderStandardBinding
|
||||
import org.koitharu.kotatsu.reader.domain.PageLoader
|
||||
import org.koitharu.kotatsu.reader.ui.ReaderState
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderAdapter
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BaseReaderFragment
|
||||
import org.koitharu.kotatsu.reader.ui.pager.ReaderPage
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.NoAnimPageTransformer
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PageHolder
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagerEventSupplier
|
||||
import org.koitharu.kotatsu.reader.ui.pager.standard.PagesAdapter
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.math.sign
|
||||
import org.koitharu.kotatsu.reader.ui.pager.BasePagerReaderFragment
|
||||
|
||||
@AndroidEntryPoint
|
||||
class VerticalReaderFragment : BaseReaderFragment<FragmentReaderStandardBinding>(),
|
||||
View.OnGenericMotionListener {
|
||||
class VerticalReaderFragment : BasePagerReaderFragment() {
|
||||
|
||||
@Inject
|
||||
lateinit var networkState: NetworkState
|
||||
|
||||
@Inject
|
||||
lateinit var pageLoader: PageLoader
|
||||
|
||||
private var pagerLifecycleDispatcher: PagerLifecycleDispatcher? = null
|
||||
|
||||
override fun onCreateViewBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
) = FragmentReaderStandardBinding.inflate(inflater, container, false)
|
||||
|
||||
override fun onViewBindingCreated(
|
||||
binding: FragmentReaderStandardBinding,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
with(binding.pager) {
|
||||
orientation = ViewPager2.ORIENTATION_VERTICAL
|
||||
adapter = readerAdapter
|
||||
offscreenPageLimit = 2
|
||||
doOnPageChanged(::notifyPageChanged)
|
||||
setOnGenericMotionListener(this@VerticalReaderFragment)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
recyclerView?.defaultFocusHighlightEnabled = false
|
||||
}
|
||||
PagerEventSupplier(this).attach()
|
||||
pagerLifecycleDispatcher = PagerLifecycleDispatcher(this).also {
|
||||
registerOnPageChangeCallback(it)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.pageAnimation.observe(viewLifecycleOwner) {
|
||||
val transformer = when (it) {
|
||||
ReaderAnimation.NONE -> NoAnimPageTransformer()
|
||||
ReaderAnimation.DEFAULT -> null
|
||||
ReaderAnimation.ADVANCED -> VerticalPageAnimTransformer()
|
||||
}
|
||||
binding.pager.setPageTransformer(transformer)
|
||||
if (transformer == null) {
|
||||
binding.pager.recyclerView?.children?.forEach { view ->
|
||||
view.resetTransformations()
|
||||
}
|
||||
}
|
||||
}
|
||||
override fun onInitPager(pager: ViewPager2) {
|
||||
super.onInitPager(pager)
|
||||
pager.orientation = ViewPager2.ORIENTATION_VERTICAL
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pagerLifecycleDispatcher = null
|
||||
requireViewBinding().pager.adapter = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onZoomIn() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomIn()
|
||||
}
|
||||
|
||||
override fun onZoomOut() {
|
||||
(viewBinding?.pager?.findCurrentViewHolder() as? PageHolder)?.onZoomOut()
|
||||
}
|
||||
|
||||
override fun onGenericMotion(v: View?, event: MotionEvent): Boolean {
|
||||
if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
|
||||
if (event.actionMasked == MotionEvent.ACTION_SCROLL) {
|
||||
val axisValue = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
val withCtrl = event.metaState and KeyEvent.META_CTRL_MASK != 0
|
||||
if (!withCtrl) {
|
||||
switchPageBy(-axisValue.sign.toInt())
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override suspend fun onPagesChanged(pages: List<ReaderPage>, pendingState: ReaderState?) =
|
||||
coroutineScope {
|
||||
val items = launch {
|
||||
requireAdapter().setItems(pages)
|
||||
yield()
|
||||
pagerLifecycleDispatcher?.invalidate()
|
||||
}
|
||||
if (pendingState != null) {
|
||||
val position = pages.indexOfFirst {
|
||||
it.chapterId == pendingState.chapterId && it.index == pendingState.page
|
||||
}
|
||||
items.join()
|
||||
if (position != -1) {
|
||||
requireViewBinding().pager.setCurrentItem(position, false)
|
||||
notifyPageChanged(position)
|
||||
} else {
|
||||
Snackbar.make(requireView(), R.string.not_found_404, Snackbar.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
} else {
|
||||
items.join()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateAdapter() = PagesAdapter(
|
||||
lifecycleOwner = viewLifecycleOwner,
|
||||
loader = pageLoader,
|
||||
settings = viewModel.readerSettings,
|
||||
networkState = networkState,
|
||||
exceptionResolver = exceptionResolver,
|
||||
)
|
||||
|
||||
override fun switchPageBy(delta: Int) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(currentItem + delta, isAnimationEnabled())
|
||||
}
|
||||
}
|
||||
|
||||
override fun switchPageTo(position: Int, smooth: Boolean) {
|
||||
with(requireViewBinding().pager) {
|
||||
setCurrentItem(
|
||||
position,
|
||||
smooth && isAnimationEnabled() && (currentItem - position).absoluteValue < SMOOTH_SCROLL_LIMIT,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentState(): ReaderState? = viewBinding?.run {
|
||||
val adapter = pager.adapter as? BaseReaderAdapter<*>
|
||||
val page = adapter?.getItemOrNull(pager.currentItem) ?: return@run null
|
||||
ReaderState(
|
||||
chapterId = page.chapterId,
|
||||
page = page.index,
|
||||
scroll = 0,
|
||||
)
|
||||
}
|
||||
|
||||
private fun notifyPageChanged(page: Int) {
|
||||
viewModel.onCurrentPageChanged(page)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val SMOOTH_SCROLL_LIMIT = 3
|
||||
}
|
||||
override fun onCreateAdvancedTransformer(): ViewPager2.PageTransformer = VerticalPageAnimTransformer()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user