@@ -12,6 +12,7 @@ package org.readium.r2.navigator.pager
12
12
import android.annotation.SuppressLint
13
13
import android.content.Context
14
14
import android.content.SharedPreferences
15
+ import android.graphics.PointF
15
16
import android.os.Bundle
16
17
import android.util.Base64
17
18
import android.util.DisplayMetrics
@@ -23,10 +24,8 @@ import androidx.core.view.ViewCompat
23
24
import androidx.fragment.app.Fragment
24
25
import androidx.lifecycle.lifecycleScope
25
26
import androidx.webkit.WebViewClientCompat
26
- import kotlinx.coroutines.delay
27
27
import kotlinx.coroutines.flow.collectLatest
28
28
import kotlinx.coroutines.launch
29
- import org.readium.r2.navigator.Navigator
30
29
import org.readium.r2.navigator.R
31
30
import org.readium.r2.navigator.R2BasicWebView
32
31
import org.readium.r2.navigator.R2WebView
@@ -49,6 +48,9 @@ class R2EpubPageFragment : Fragment() {
49
48
internal val link: Link ?
50
49
get() = requireArguments().getParcelable(" link" )
51
50
51
+ private val positionCount: Long
52
+ get() = requireArguments().getLong(" positionCount" )
53
+
52
54
var webView: R2WebView ? = null
53
55
private set
54
56
@@ -58,6 +60,8 @@ class R2EpubPageFragment : Fragment() {
58
60
private var _binding : ViewpagerFragmentEpubBinding ? = null
59
61
private val binding get() = _binding !!
60
62
63
+ private var isLoading: Boolean = false
64
+
61
65
@SuppressLint(" SetJavaScriptEnabled" )
62
66
override fun onCreateView (inflater : LayoutInflater , container : ViewGroup ? , savedInstanceState : Bundle ? ): View ? {
63
67
val navigatorFragment = parentFragment as EpubNavigatorFragment
@@ -68,6 +72,7 @@ class R2EpubPageFragment : Fragment() {
68
72
val webView = binding.webView
69
73
this .webView = webView
70
74
75
+ webView.visibility = View .INVISIBLE
71
76
webView.navigator = navigatorFragment
72
77
webView.listener = navigatorFragment.webViewListener
73
78
webView.preferences = preferences
@@ -127,71 +132,13 @@ class R2EpubPageFragment : Fragment() {
127
132
128
133
webView.listener.onResourceLoaded(link, webView, url)
129
134
130
- val epubNavigator = (webView.navigator as ? EpubNavigatorFragment )
131
- val currentFragment: R2EpubPageFragment ? =
132
- (epubNavigator?.resourcePager?.adapter as ? R2PagerAdapter )?.getCurrentFragment() as ? R2EpubPageFragment
133
-
134
- if (currentFragment != null && this @R2EpubPageFragment.tag == currentFragment.tag) {
135
- val locator = epubNavigator.pendingLocator
136
- epubNavigator.pendingLocator = null
137
- var locations = locator?.locations
138
-
139
- // TODO this seems to be needed, will need to test more
140
- if (url != null && url.indexOf(" #" ) > 0 ) {
141
- val id = url.substringAfterLast(" #" )
142
- locations = Locator .Locations (fragments = listOf (id))
135
+ // To make sure the page is properly laid out before jumping to the target locator,
136
+ // we execute a dummy JavaScript and wait for the callback result.
137
+ webView.evaluateJavascript(" true" ) {
138
+ viewLifecycleOwner.lifecycleScope.launchWhenCreated {
139
+ onLoadPage()
143
140
}
144
-
145
- val currentWebView = currentFragment.webView
146
- if (currentWebView != null && locations != null ) {
147
-
148
- lifecycleScope.launchWhenStarted {
149
- // FIXME: We need a better way to wait, because if the value is too low it fails
150
- delay(200 )
151
-
152
- val text = locator?.text
153
- if (text?.highlight != null ) {
154
- if (currentWebView.scrollToText(text)) {
155
- return @launchWhenStarted
156
- }
157
-
158
- // The delay is necessary before falling back on the other
159
- // locations, because scrollToText() is moving the scroll position
160
- // while looking for the text snippet.
161
- delay(100 )
162
- }
163
-
164
- val htmlId = locations.htmlId
165
- if (htmlId != null && currentWebView.scrollToId(htmlId)) {
166
- return @launchWhenStarted
167
- }
168
-
169
- var progression = locations.progression
170
- if (progression != null ) {
171
- // We need to reverse the progression with RTL because the Web View
172
- // always scrolls from left to right, no matter the reading direction.
173
- progression =
174
- if (webView.scrollMode || navigatorFragment.readingProgression == ReadingProgression .LTR ) progression
175
- else 1 - progression
176
-
177
- if (webView.scrollMode) {
178
- currentWebView.scrollToPosition(progression)
179
-
180
- } else {
181
- // Figure out the target web view "page" from the requested
182
- // progression.
183
- var item = (progression * currentWebView.numPages).roundToInt()
184
- if (navigatorFragment.readingProgression == ReadingProgression .RTL && item > 0 ) {
185
- item - = 1
186
- }
187
- currentWebView.setCurrentItem(item, false )
188
- }
189
- }
190
- }
191
- }
192
-
193
141
}
194
- webView.listener.onPageLoaded()
195
142
}
196
143
197
144
// prevent favicon.ico to be loaded, this was causing NullPointerException in NanoHttp
@@ -237,10 +184,19 @@ class R2EpubPageFragment : Fragment() {
237
184
false
238
185
}
239
186
240
- resourceUrl?.let { webView.loadUrl(it) }
187
+ resourceUrl?.let {
188
+ isLoading = true
189
+ webView.loadUrl(it)
190
+ }
241
191
242
192
setupPadding()
243
193
194
+ // Forward a tap event when the web view is not ready to propagate the taps. This allows
195
+ // to toggle a navigation UI while a page is loading, for example.
196
+ binding.root.setOnClickListenerWithPoint { _, point ->
197
+ webView.listener.onTap(point)
198
+ }
199
+
244
200
return containerView
245
201
}
246
202
@@ -312,15 +268,96 @@ class R2EpubPageFragment : Fragment() {
312
268
internal val paddingTop: Int get() = containerView.paddingTop
313
269
internal val paddingBottom: Int get() = containerView.paddingBottom
314
270
271
+ private val isCurrentResource: Boolean get() {
272
+ val epubNavigator = webView?.navigator as ? EpubNavigatorFragment ? : return false
273
+ val currentFragment = (epubNavigator.resourcePager.adapter as ? R2PagerAdapter )?.getCurrentFragment() as ? R2EpubPageFragment ? : return false
274
+ return tag == currentFragment.tag
275
+ }
276
+
277
+ private suspend fun onLoadPage () {
278
+ if (! isLoading) return
279
+ isLoading = false
280
+
281
+ val webView = requireNotNull(webView)
282
+ webView.visibility = View .VISIBLE
283
+
284
+ if (isCurrentResource) {
285
+ val epubNavigator = requireNotNull(webView.navigator as ? EpubNavigatorFragment )
286
+ val locator = epubNavigator.pendingLocator
287
+ epubNavigator.pendingLocator = null
288
+ if (locator != null ) {
289
+ loadLocator(locator)
290
+ }
291
+
292
+ webView.listener.onPageLoaded()
293
+ }
294
+ }
295
+
296
+ private suspend fun loadLocator (locator : Locator ) {
297
+ val webView = requireNotNull(webView)
298
+ val epubNavigator = requireNotNull(webView.navigator as ? EpubNavigatorFragment )
299
+
300
+ val text = locator.text
301
+ if (text.highlight != null ) {
302
+ if (webView.scrollToText(text)) {
303
+ return
304
+ }
305
+ }
306
+
307
+ val htmlId = locator.locations.htmlId
308
+ if (htmlId != null && webView.scrollToId(htmlId)) {
309
+ return
310
+ }
311
+
312
+ var progression = locator.locations.progression
313
+ if (progression != null ) {
314
+ // We need to reverse the progression with RTL because the Web View
315
+ // always scrolls from left to right, no matter the reading direction.
316
+ progression =
317
+ if (webView.scrollMode || epubNavigator.readingProgression == ReadingProgression .LTR ) progression
318
+ else 1 - progression
319
+
320
+ if (webView.scrollMode) {
321
+ webView.scrollToPosition(progression)
322
+
323
+ } else {
324
+ // Figure out the target web view "page" from the requested
325
+ // progression.
326
+ var item = (progression * webView.numPages).roundToInt()
327
+ if (epubNavigator.readingProgression == ReadingProgression .RTL && item > 0 ) {
328
+ item - = 1
329
+ }
330
+ webView.setCurrentItem(item, false )
331
+ }
332
+ }
333
+ }
334
+
315
335
companion object {
316
- fun newInstance (url : String , link : Link ? = null): R2EpubPageFragment =
336
+ fun newInstance (url : String , link : Link ? = null, positionCount : Int = 0 ): R2EpubPageFragment =
317
337
R2EpubPageFragment ().apply {
318
338
arguments = Bundle ().apply {
319
339
putString(" url" , url)
320
340
putParcelable(" link" , link)
341
+ putLong(" positionCount" , positionCount.toLong())
321
342
}
322
343
}
323
344
}
324
345
}
325
346
347
+ /* *
348
+ * Same as setOnClickListener, but will also report the tap point in the view.
349
+ */
350
+ private fun View.setOnClickListenerWithPoint (action : (View , PointF ) -> Unit ) {
351
+ var point = PointF ()
326
352
353
+ setOnTouchListener { v, event ->
354
+ if (event.action == MotionEvent .ACTION_DOWN ) {
355
+ point = PointF (event.x, event.y)
356
+ }
357
+ false
358
+ }
359
+
360
+ setOnClickListener {
361
+ action(it, point)
362
+ }
363
+ }
0 commit comments