@@ -31,7 +31,7 @@ import androidx.lifecycle.Lifecycle
3131import androidx.lifecycle.lifecycleScope
3232import androidx.lifecycle.repeatOnLifecycle
3333import androidx.lifecycle.withStarted
34- import androidx.viewpager .widget.ViewPager
34+ import androidx.viewpager2 .widget.ViewPager2
3535import kotlin.math.ceil
3636import kotlin.reflect.KClass
3737import kotlinx.coroutines.Job
@@ -425,7 +425,7 @@ public class EpubNavigatorFragment internal constructor(
425425 " The parent view of the EPUB `resourcePager` must be a ConstraintLayout"
426426 }
427427 // We need to null out the adapter explicitly, otherwise the page fragments will leak.
428- resourcePager.adapter = null
428+ resourcePager.setAdapter( null )
429429 parent.removeView(resourcePager)
430430
431431 resourcePager = R2ViewPager (requireContext())
@@ -434,17 +434,96 @@ public class EpubNavigatorFragment internal constructor(
434434 EpubLayout .REFLOWABLE , null -> R2ViewPager .PublicationType .EPUB
435435 EpubLayout .FIXED -> R2ViewPager .PublicationType .FXL
436436 }
437+
438+ // Configure ViewPager orientation based on scroll settings
439+ if (viewModel.layout == EpubLayout .REFLOWABLE && viewModel.settings.value.scroll) {
440+ resourcePager.configureForVerticalScrolling()
441+ } else {
442+ resourcePager.configureForHorizontalPaging()
443+ }
444+
437445 resourcePager.setBackgroundColor(viewModel.settings.value.effectiveBackgroundColor)
438446 // Let the page views handle the keyboard events.
439447 resourcePager.isFocusable = false
440- resourcePager.addOnPageChangeListener(PageChangeListener ())
448+ resourcePager.registerOnPageChangeCallback(PageChangeListener ())
449+
450+ // Set proper ConstraintLayout parameters
451+ val layoutParams = ConstraintLayout .LayoutParams (
452+ ConstraintLayout .LayoutParams .MATCH_CONSTRAINT ,
453+ ConstraintLayout .LayoutParams .MATCH_CONSTRAINT
454+ ).apply {
455+ topToTop = ConstraintLayout .LayoutParams .PARENT_ID
456+ bottomToBottom = ConstraintLayout .LayoutParams .PARENT_ID
457+ startToStart = ConstraintLayout .LayoutParams .PARENT_ID
458+ endToEnd = ConstraintLayout .LayoutParams .PARENT_ID
459+ }
460+ resourcePager.layoutParams = layoutParams
441461
442462 parent.addView(resourcePager)
443463
444464 resetResourcePagerAdapter()
445465 }
446466
447- private inner class PageChangeListener : ViewPager .SimpleOnPageChangeListener () {
467+ private inner class PageChangeListener : ViewPager2 .OnPageChangeCallback () {
468+ private var hasPresetScrollPosition = false
469+
470+ override fun onPageScrollStateChanged (state : Int ) {
471+ when (state) {
472+ ViewPager2 .SCROLL_STATE_IDLE -> {
473+ // Reset flag when swipe ends
474+ hasPresetScrollPosition = false
475+ }
476+ else -> Unit
477+ }
478+ }
479+
480+ override fun onPageScrolled (
481+ position : Int ,
482+ positionOffset : Float ,
483+ positionOffsetPixels : Int ,
484+ ) {
485+ // Only process when swipe has started and scroll position hasn't been set yet
486+ if (positionOffset > 0f && ! hasPresetScrollPosition) {
487+ val currentPosition = resourcePager.currentItem
488+
489+ // When swiping to previous page (position is less than current)
490+ if (position < currentPosition) {
491+ // Find target page fragment and set to last page
492+ val targetFragment = fragmentAt(position) as ? R2EpubPageFragment
493+ targetFragment?.webView?.let { webView ->
494+ if (viewModel.isScrollEnabled.value) {
495+ webView.scrollToEnd()
496+ } else if (webView.numPages > 1 ) {
497+ if (resourcePager.isRtl()) {
498+ webView.setCurrentItem(0 , false )
499+ } else {
500+ webView.setCurrentItem(webView.numPages - 1 , false )
501+ }
502+ }
503+ }
504+ hasPresetScrollPosition = true
505+ }
506+ // When swiping to next page (position + 1 is greater than current)
507+ else if (position + 1 > currentPosition) {
508+ // Find target page fragment and set to first page
509+ val targetFragment = fragmentAt(position + 1 ) as ? R2EpubPageFragment
510+ targetFragment?.webView?.let { webView ->
511+ if (viewModel.isScrollEnabled.value) {
512+ webView.scrollToStart()
513+ } else if (webView.numPages > 1 ) {
514+ if (resourcePager.isRtl()) {
515+ webView.setCurrentItem(webView.numPages - 1 , false )
516+ } else {
517+ webView.setCurrentItem(0 , false )
518+ }
519+ }
520+ }
521+
522+ hasPresetScrollPosition = true
523+ }
524+ }
525+ }
526+
448527 override fun onPageSelected (position : Int ) {
449528 currentReflowablePageFragment?.webView?.let { webView ->
450529 if (viewModel.isScrollEnabled.value) {
@@ -456,16 +535,17 @@ public class EpubNavigatorFragment internal constructor(
456535 webView.scrollToEnd()
457536 }
458537 } else {
459- if (currentPagerPosition < position ) {
460- // handle swipe LEFT
461- webView.setCurrentItem(0 , false )
462- } else if (currentPagerPosition > position) {
463- // handle swipe RIGHT
464- webView.setCurrentItem(webView.numPages - 1 , false )
538+ if (! hasPresetScrollPosition ) {
539+ if (currentPagerPosition < position) {
540+ webView.setCurrentItem(0 , false )
541+ } else if (currentPagerPosition > position) {
542+ webView.setCurrentItem(webView.numPages - 1 , false )
543+ }
465544 }
466545 }
467546 }
468- currentPagerPosition = position // Update current position
547+
548+ currentPagerPosition = position
469549
470550 notifyCurrentLocation()
471551 }
@@ -474,23 +554,23 @@ public class EpubNavigatorFragment internal constructor(
474554 private fun resetResourcePagerAdapter () {
475555 adapter = when (publication.metadata.presentation.layout) {
476556 EpubLayout .REFLOWABLE , null -> {
477- R2PagerAdapter (childFragmentManager , resourcesSingle)
557+ R2PagerAdapter (this , resourcesSingle)
478558 }
479559 EpubLayout .FIXED -> {
480560 when (viewModel.dualPageMode) {
481561 // FIXME: Properly implement DualPage.AUTO depending on the device orientation.
482562 DualPage .OFF , DualPage .AUTO -> {
483- R2PagerAdapter (childFragmentManager , resourcesSingle)
563+ R2PagerAdapter (this , resourcesSingle)
484564 }
485565 DualPage .ON -> {
486- R2PagerAdapter (childFragmentManager , resourcesDouble)
566+ R2PagerAdapter (this , resourcesDouble)
487567 }
488568 }
489569 }
490570 }
491571 adapter.listener = PagerAdapterListener ()
492- resourcePager.adapter = adapter
493- resourcePager.direction = overflow.value.readingProgression
572+ resourcePager.setAdapter( adapter)
573+ resourcePager.readingProgression = overflow.value.readingProgression
494574 resourcePager.layoutDirection = when (settings.value.readingProgression) {
495575 ReadingProgression .RTL -> LayoutDirection .RTL
496576 ReadingProgression .LTR -> LayoutDirection .LTR
@@ -625,11 +705,13 @@ public class EpubNavigatorFragment internal constructor(
625705 else -> false
626706 }
627707 } ? : return
708+
628709 val (index, _) = page
629710
630711 if (resourcePager.currentItem != index) {
631- resourcePager.currentItem = index
712+ resourcePager.setCurrentItem( index, false )
632713 }
714+
633715 r2PagerAdapter?.loadLocatorAt(index, locator)
634716 }
635717
@@ -903,7 +985,7 @@ public class EpubNavigatorFragment internal constructor(
903985
904986 private fun goToNextResource (jump : Boolean , animated : Boolean ): Boolean {
905987 val adapter = resourcePager.adapter ? : return false
906- if (resourcePager.currentItem >= adapter.count - 1 ) {
988+ if (resourcePager.currentItem >= adapter.itemCount - 1 ) {
907989 return false
908990 }
909991
0 commit comments