diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePagerReaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePagerReaderFragment.kt new file mode 100644 index 000000000..052b9f837 --- /dev/null +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/BasePagerReaderFragment.kt @@ -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(), + 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, 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 + } +} diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt index e9ccb68ab..46954215a 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/reversed/ReversedReaderFragment.kt @@ -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(), - 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 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, 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, 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)) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt index 70a2cbc28..628a0ea23 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/standard/PagerReaderFragment.kt @@ -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(), - 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, 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() diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/vertical/VerticalReaderFragment.kt b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/vertical/VerticalReaderFragment.kt index f221e0efa..717a97564 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/vertical/VerticalReaderFragment.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/reader/ui/pager/vertical/VerticalReaderFragment.kt @@ -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(), - 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, 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() } diff --git a/app/src/main/res/layout/fragment_reader_standard.xml b/app/src/main/res/layout/fragment_reader_pager.xml similarity index 100% rename from app/src/main/res/layout/fragment_reader_standard.xml rename to app/src/main/res/layout/fragment_reader_pager.xml