diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ecfb6ab12..fae3c5febd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ All notable changes to this project will be documented in this file. Take a look * New `EPUBNavigatorFragment.Configuration.useReadiumCssFontSize` option to revert to the 2.2.0 strategy for setting the font size of reflowable EPUB publications. * The native font size strategy introduced in 2.3.0 uses the Android web view's [`WebSettings.textZoom`](https://developer.android.com/reference/android/webkit/WebSettings#setTextZoom(int)) property to adjust the font size. 2.2.0 was using Readium CSS's [`--USER__fontSize` variable](https://readium.org/readium-css/docs/CSS12-user_prefs.html#font-size). * `WebSettings.textZoom` will work with more publications than `--USER__fontSize`, even the ones poorly authored. However the page width is not adjusted when changing the font size to keep the optimal line length. +* Scroll mode: jumping between two EPUB resources with a horizontal swipe triggers the `Navigator.Listener.onJumpToLocator()` callback. + * This can be used to allow the user to go back to their previous location if they swiped across chapters by mistake. #### Changed diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt index 88062b4b51..d4e6363899 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt @@ -65,8 +65,8 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte fun onProgressionChanged() fun onHighlightActivated(id: String) fun onHighlightAnnotationMarkActivated(id: String) - fun goForward(animated: Boolean = false, completion: () -> Unit = {}): Boolean - fun goBackward(animated: Boolean = false, completion: () -> Unit = {}): Boolean + fun goForward(animated: Boolean = false, completion: () -> Unit = {}): Boolean = false + fun goBackward(animated: Boolean = false, completion: () -> Unit = {}): Boolean = false /** * Returns the custom [ActionMode.Callback] to be used with the text selection menu. @@ -75,15 +75,23 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte @InternalReadiumApi fun javascriptInterfacesForResource(link: Link): Map = emptyMap() - @InternalReadiumApi fun shouldOverrideUrlLoading(webView: WebView, request: WebResourceRequest): Boolean = false - @InternalReadiumApi fun shouldInterceptRequest(webView: WebView, request: WebResourceRequest): WebResourceResponse? = null - @InternalReadiumApi fun resourceAtUrl(url: String): Resource? = null + + /** + * Requests to load the next resource in the reading order. + * + * @param jump Indicates whether it's a discontinuous jump from the current locator. Used + * for scroll mode. + */ + @InternalReadiumApi + fun goToNextResource(jump: Boolean, animated: Boolean): Boolean = false + @InternalReadiumApi + fun goToPreviousResource(jump: Boolean, animated: Boolean): Boolean = false } var listener: Listener? = null @@ -183,22 +191,31 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte val listener = listener ?: return@launch listener.onScroll() - fun goRight() { + val isRtl = (listener.readingProgression == ReadingProgression.RTL) + + fun goRight(jump: Boolean) { if (listener.readingProgression == ReadingProgression.RTL) { - listener.goBackward(animated = animated) + listener.goBackward(animated = animated) // Legacy + listener.goToPreviousResource(jump = jump, animated = animated) } else { - listener.goForward(animated = animated) + listener.goForward(animated = animated) // Legacy + listener.goToNextResource(jump = jump, animated = animated) } } - if (scrollMode || !this@R2BasicWebView.canScrollHorizontally(1)) { - goRight() - } else { - runJavaScript("readium.scrollRight();") { success -> - if (!success.toBoolean()) { - goRight() + when { + scrollMode -> + goRight(jump = true) + + !this@R2BasicWebView.canScrollHorizontally(1) -> + goRight(jump = false) + + else -> + runJavaScript("readium.scrollRight();") { success -> + if (!success.toBoolean()) { + goRight(jump = false) + } } - } } } } @@ -209,22 +226,29 @@ open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebView(conte val listener = listener ?: return@launch listener.onScroll() - fun goLeft() { + fun goLeft(jump: Boolean) { if (listener.readingProgression == ReadingProgression.RTL) { - listener.goForward(animated = animated) + listener.goForward(animated = animated) // legacy + listener.goToNextResource(jump = jump, animated = animated) } else { - listener.goBackward(animated = animated) + listener.goBackward(animated = animated) // legacy + listener.goToPreviousResource(jump = jump, animated = animated) } } - if (scrollMode || !this@R2BasicWebView.canScrollHorizontally(-1)) { - goLeft() - } else { - runJavaScript("readium.scrollLeft();") { success -> - if (!success.toBoolean()) { - goLeft() + when { + scrollMode -> + goLeft(jump = true) + + !this@R2BasicWebView.canScrollHorizontally(-1) -> + goLeft(jump = false) + + else -> + runJavaScript("readium.scrollLeft();") { success -> + if (!success.toBoolean()) { + goLeft(jump = false) + } } - } } } } diff --git a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt index fd85bbe7f7..ffb45d513a 100644 --- a/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt +++ b/readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt @@ -763,11 +763,13 @@ class EpubNavigatorFragment internal constructor( r2Activity?.highlightAnnotationMarkActivated(id) } - override fun goForward(animated: Boolean, completion: () -> Unit): Boolean = - goToNextResource(animated, completion) + override fun goToPreviousResource(jump: Boolean, animated: Boolean): Boolean { + return this@EpubNavigatorFragment.goToPreviousResource(jump = jump, animated = animated) + } - override fun goBackward(animated: Boolean, completion: () -> Unit): Boolean = - goToPreviousResource(animated, completion) + override fun goToNextResource(jump: Boolean, animated: Boolean): Boolean { + return this@EpubNavigatorFragment.goToNextResource(jump = jump, animated = animated) + } override val selectionActionModeCallback: ActionMode.Callback? get() = config.selectionActionModeCallback @@ -791,7 +793,7 @@ class EpubNavigatorFragment internal constructor( override fun goForward(animated: Boolean, completion: () -> Unit): Boolean { if (publication.metadata.presentation.layout == EpubLayout.FIXED) { - return goToNextResource(animated, completion) + return goToNextResource(jump = false, animated = animated, completion) } val webView = currentReflowablePageFragment?.webView ?: return false @@ -809,7 +811,7 @@ class EpubNavigatorFragment internal constructor( override fun goBackward(animated: Boolean, completion: () -> Unit): Boolean { if (publication.metadata.presentation.layout == EpubLayout.FIXED) { - return goToPreviousResource(animated, completion) + return goToPreviousResource(jump = false, animated = animated, completion) } val webView = currentReflowablePageFragment?.webView ?: return false @@ -825,12 +827,16 @@ class EpubNavigatorFragment internal constructor( return true } - private fun goToNextResource(animated: Boolean, completion: () -> Unit): Boolean { + private fun goToNextResource(jump: Boolean, animated: Boolean, completion: () -> Unit = {}): Boolean { val adapter = resourcePager.adapter ?: return false if (resourcePager.currentItem >= adapter.count - 1) { return false } + if (jump) { + locatorToNextResource()?.let { listener?.onJumpToLocator(it) } + } + resourcePager.setCurrentItem(resourcePager.currentItem + 1, animated) currentReflowablePageFragment?.webView?.let { webView -> @@ -845,11 +851,15 @@ class EpubNavigatorFragment internal constructor( return true } - private fun goToPreviousResource(animated: Boolean, completion: () -> Unit): Boolean { + private fun goToPreviousResource(jump: Boolean, animated: Boolean, completion: () -> Unit = {}): Boolean { if (resourcePager.currentItem <= 0) { return false } + if (jump) { + locatorToPreviousResource()?.let { listener?.onJumpToLocator(it) } + } + resourcePager.setCurrentItem(resourcePager.currentItem - 1, animated) currentReflowablePageFragment?.webView?.let { webView -> @@ -864,6 +874,16 @@ class EpubNavigatorFragment internal constructor( return true } + private fun locatorToPreviousResource(): Locator? = + locatorToResourceAtIndex(resourcePager.currentItem - 1) + + private fun locatorToNextResource(): Locator? = + locatorToResourceAtIndex(resourcePager.currentItem + 1) + + private fun locatorToResourceAtIndex(index: Int): Locator? = + publication.readingOrder.getOrNull(index) + ?.let { publication.locatorFromLink(it) } + private val r2PagerAdapter: R2PagerAdapter? get() = if (::resourcePager.isInitialized) resourcePager.adapter as? R2PagerAdapter else null