Skip to content

Commit 611ea81

Browse files
authored
Prevent reporting the current position while loading a locator (#487)
1 parent d335bde commit 611ea81

File tree

7 files changed

+75
-26
lines changed

7 files changed

+75
-26
lines changed

readium/navigator/src/main/java/org/readium/r2/navigator/R2BasicWebView.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,12 @@ internal open class R2BasicWebView(context: Context, attrs: AttributeSet) : WebV
5454

5555
interface Listener {
5656
val readingProgression: ReadingProgression
57-
fun onResourceLoaded(link: Link?, webView: R2BasicWebView, url: String?) {}
58-
fun onPageLoaded() {}
57+
58+
/** Called when the resource content is loaded in the web view. */
59+
fun onResourceLoaded(webView: R2BasicWebView, link: Link) {}
60+
61+
/** Called when the target page of the resource is loaded in the web view. */
62+
fun onPageLoaded(webView: R2BasicWebView, link: Link) {}
5963
fun onPageChanged(pageIndex: Int, totalPages: Int, url: String) {}
6064
fun onPageEnded(end: Boolean) {}
6165
fun onTap(point: PointF): Boolean = false

readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorFragment.kt

+26-4
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,19 @@ public class EpubNavigatorFragment internal constructor(
265265

266266
public interface Listener : OverflowableNavigator.Listener, HyperlinkNavigator.Listener
267267

268+
private sealed class State {
269+
/** The navigator just started and didn't load any resource yet. */
270+
object Initializing : State()
271+
272+
/** The navigator is jumping to the resource at `locator`. */
273+
data class Loading(val locator: Locator) : State()
274+
275+
/** The navigator is idle and ready for interactions. */
276+
object Ready : State()
277+
}
278+
279+
private var state: State = State.Initializing
280+
268281
// Configurable
269282

270283
override val settings: StateFlow<EpubSettings> get() = viewModel.settings
@@ -595,6 +608,8 @@ public class EpubNavigatorFragment internal constructor(
595608
@Suppress("NAME_SHADOWING")
596609
val locator = publication.normalizeLocator(locator)
597610

611+
state = State.Loading(locator)
612+
598613
listener?.onJumpToLocator(locator)
599614

600615
val href = locator.href.removeFragment()
@@ -759,12 +774,17 @@ public class EpubNavigatorFragment internal constructor(
759774
override val readingProgression: ReadingProgression
760775
get() = viewModel.readingProgression
761776

762-
override fun onResourceLoaded(link: Link?, webView: R2BasicWebView, url: String?) {
763-
run(viewModel.onResourceLoaded(link, webView))
777+
override fun onResourceLoaded(webView: R2BasicWebView, link: Link) {
778+
run(viewModel.onResourceLoaded(webView, link))
764779
}
765780

766-
override fun onPageLoaded() {
781+
override fun onPageLoaded(webView: R2BasicWebView, link: Link) {
767782
paginationListener?.onPageLoaded()
783+
784+
if (state is State.Initializing || (state as? State.Loading)?.locator?.href == link.url()) {
785+
state = State.Ready
786+
}
787+
768788
notifyCurrentLocation()
769789
}
770790

@@ -1028,7 +1048,9 @@ public class EpubNavigatorFragment internal constructor(
10281048
debounceLocationNotificationJob = viewLifecycleOwner.lifecycleScope.launch {
10291049
delay(100L)
10301050

1031-
if (currentReflowablePageFragment?.isLoaded?.value == false) {
1051+
// We don't want to notify the current location if the navigator is still loading a
1052+
// locator, to avoid notifying intermediate locations.
1053+
if (currentReflowablePageFragment?.isLoaded?.value == false || state != State.Ready) {
10321054
return@launch
10331055
}
10341056

readium/navigator/src/main/java/org/readium/r2/navigator/epub/EpubNavigatorViewModel.kt

+7-9
Original file line numberDiff line numberDiff line change
@@ -140,20 +140,18 @@ internal class EpubNavigatorViewModel(
140140
.launchIn(viewModelScope)
141141
}
142142

143-
fun onResourceLoaded(link: Link?, webView: R2BasicWebView): RunScriptCommand {
143+
fun onResourceLoaded(webView: R2BasicWebView, link: Link): RunScriptCommand {
144144
val templates = decorationTemplates.toJSON().toString()
145145
.replace("\\n", " ")
146146
var script = "readium.registerDecorationTemplates($templates);\n"
147147

148-
if (link != null) {
149-
for ((group, decorations) in decorations) {
150-
val changes = decorations
151-
.filter { it.locator.href == link.url() }
152-
.map { DecorationChange.Added(it) }
148+
for ((group, decorations) in decorations) {
149+
val changes = decorations
150+
.filter { it.locator.href == link.url() }
151+
.map { DecorationChange.Added(it) }
153152

154-
val groupScript = changes.javascriptForGroup(group, decorationTemplates) ?: continue
155-
script += "$groupScript\n"
156-
}
153+
val groupScript = changes.javascriptForGroup(group, decorationTemplates) ?: continue
154+
script += "$groupScript\n"
157155
}
158156

159157
return RunScriptCommand(script, scope = RunScriptCommand.Scope.WebView(webView))

readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2EpubPageFragment.kt

+6-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ internal class R2EpubPageFragment : Fragment() {
209209
override fun onPageFinished(view: WebView?, url: String?) {
210210
super.onPageFinished(view, url)
211211

212-
webView.listener?.onResourceLoaded(link, webView, url)
212+
link?.let {
213+
webView.listener?.onResourceLoaded(webView, it)
214+
}
213215

214216
// To make sure the page is properly laid out before jumping to the target locator,
215217
// we execute a dummy JavaScript and wait for the callback result.
@@ -362,7 +364,9 @@ internal class R2EpubPageFragment : Fragment() {
362364
}
363365
.also { pendingLocator = null }
364366

365-
webView.listener?.onPageLoaded()
367+
link?.let {
368+
webView.listener?.onPageLoaded(webView, it)
369+
}
366370
}
367371
}
368372
}

readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2FXLPageFragment.kt

+25-7
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import org.readium.r2.navigator.epub.EpubNavigatorFragment
2929
import org.readium.r2.navigator.epub.EpubNavigatorViewModel
3030
import org.readium.r2.navigator.epub.fxl.R2FXLLayout
3131
import org.readium.r2.navigator.epub.fxl.R2FXLOnDoubleTapListener
32+
import org.readium.r2.shared.publication.Link
3233
import org.readium.r2.shared.util.Url
3334

3435
internal class R2FXLPageFragment : Fragment() {
@@ -39,6 +40,12 @@ internal class R2FXLPageFragment : Fragment() {
3940
private val secondResourceUrl: Url?
4041
get() = BundleCompat.getParcelable(requireArguments(), "secondUrl", Url::class.java)
4142

43+
private val firstResourceLink: Link?
44+
get() = BundleCompat.getParcelable(requireArguments(), "firstLink", Link::class.java)
45+
46+
private val secondResourceLink: Link?
47+
get() = BundleCompat.getParcelable(requireArguments(), "secondLink", Link::class.java)
48+
4249
private var webViews = mutableListOf<R2BasicWebView>()
4350

4451
private var _doubleBinding: ReadiumNavigatorFragmentFxllayoutDoubleBinding? = null
@@ -75,8 +82,8 @@ internal class R2FXLPageFragment : Fragment() {
7582
val left = doubleBinding.firstWebView
7683
val right = doubleBinding.secondWebView
7784

78-
setupWebView(left, firstResourceUrl)
79-
setupWebView(right, secondResourceUrl)
85+
setupWebView(left, firstResourceLink, firstResourceUrl)
86+
setupWebView(right, secondResourceLink, secondResourceUrl)
8087

8188
r2FXLLayout.addOnDoubleTapListener(R2FXLOnDoubleTapListener(true))
8289
r2FXLLayout.addOnTapListener(object : R2FXLLayout.OnTapListener {
@@ -100,7 +107,7 @@ internal class R2FXLPageFragment : Fragment() {
100107

101108
val webview = singleBinding.webViewSingle
102109

103-
setupWebView(webview, firstResourceUrl)
110+
setupWebView(webview, firstResourceLink, firstResourceUrl)
104111

105112
r2FXLLayout.addOnDoubleTapListener(R2FXLOnDoubleTapListener(true))
106113
r2FXLLayout.addOnTapListener(object : R2FXLLayout.OnTapListener {
@@ -136,7 +143,7 @@ internal class R2FXLPageFragment : Fragment() {
136143
}
137144

138145
@SuppressLint("SetJavaScriptEnabled")
139-
private fun setupWebView(webView: R2BasicWebView, resourceUrl: Url?) {
146+
private fun setupWebView(webView: R2BasicWebView, link: Link?, resourceUrl: Url?) {
140147
webViews.add(webView)
141148
navigator?.let {
142149
webView.listener = it.webViewListener
@@ -167,6 +174,15 @@ internal class R2FXLPageFragment : Fragment() {
167174

168175
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? =
169176
(webView as? R2BasicWebView)?.shouldInterceptRequest(view, request)
177+
178+
override fun onPageFinished(view: WebView?, url: String?) {
179+
super.onPageFinished(view, url)
180+
181+
if (link != null) {
182+
webView.listener?.onResourceLoaded(webView, link)
183+
webView.listener?.onPageLoaded(webView, link)
184+
}
185+
}
170186
}
171187
webView.isHapticFeedbackEnabled = false
172188
webView.isLongClickable = false
@@ -179,11 +195,13 @@ internal class R2FXLPageFragment : Fragment() {
179195

180196
companion object {
181197

182-
fun newInstance(url: Url?, url2: Url? = null): R2FXLPageFragment =
198+
fun newInstance(left: Pair<Link, Url>?, right: Pair<Link, Url>? = null): R2FXLPageFragment =
183199
R2FXLPageFragment().apply {
184200
arguments = Bundle().apply {
185-
putParcelable("firstUrl", url)
186-
putParcelable("secondUrl", url2)
201+
putParcelable("firstLink", left?.first)
202+
putParcelable("firstUrl", left?.second)
203+
putParcelable("secondLink", right?.first)
204+
putParcelable("secondUrl", right?.second)
187205
}
188206
}
189207
}

readium/navigator/src/main/java/org/readium/r2/navigator/pager/R2PagerAdapter.kt

+5-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import androidx.collection.LongSparseArray
1616
import androidx.collection.forEach
1717
import androidx.fragment.app.Fragment
1818
import androidx.fragment.app.FragmentManager
19+
import org.readium.r2.navigator.extensions.let
1920
import org.readium.r2.shared.publication.Link
2021
import org.readium.r2.shared.publication.Locator
2122
import org.readium.r2.shared.util.AbsoluteUrl
@@ -83,7 +84,10 @@ internal class R2PagerAdapter internal constructor(
8384
)
8485
}
8586
is PageResource.EpubFxl -> {
86-
R2FXLPageFragment.newInstance(resource.leftUrl, resource.rightUrl)
87+
R2FXLPageFragment.newInstance(
88+
left = let(resource.leftLink, resource.leftUrl) { l, u -> Pair(l, u) },
89+
right = let(resource.rightLink, resource.rightUrl) { l, u -> Pair(l, u) }
90+
)
8791
}
8892
is PageResource.Cbz -> {
8993
fm.fragmentFactory

readium/shared/src/main/java/org/readium/r2/shared/publication/Publication.kt

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import kotlin.reflect.KClass
1515
import kotlinx.parcelize.Parcelize
1616
import org.json.JSONObject
1717
import org.readium.r2.shared.*
18-
import org.readium.r2.shared.extensions.*
1918
import org.readium.r2.shared.publication.epub.listOfAudioClips
2019
import org.readium.r2.shared.publication.epub.listOfVideoClips
2120
import org.readium.r2.shared.publication.services.CacheService

0 commit comments

Comments
 (0)