Skip to content

Commit 50173a7

Browse files
committed
Revisit guided navigation tree
1 parent 33abc52 commit 50173a7

File tree

27 files changed

+1324
-496
lines changed

27 files changed

+1324
-496
lines changed

demos/navigator/src/main/java/org/readium/demo/navigator/DemoViewModel.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.readium.demo.navigator.reader.SelectNavigatorItem
2323
import org.readium.demo.navigator.reader.SelectNavigatorViewModel
2424
import org.readium.demo.navigator.reader.fixedConfig
2525
import org.readium.demo.navigator.reader.reflowableConfig
26+
import org.readium.navigator.media.readaloud.NullTtsEngineProvider
2627
import org.readium.navigator.media.readaloud.ReadAloudNavigatorFactory
2728
import org.readium.navigator.web.fixedlayout.FixedWebRenditionFactory
2829
import org.readium.navigator.web.reflowable.ReflowableWebRenditionFactory
@@ -127,7 +128,8 @@ class DemoViewModel(
127128
ReadAloudNavigatorFactory(
128129
application = application,
129130
publication = publication,
130-
audioEngineProvider = audioEngineProvider
131+
audioEngineProvider = audioEngineProvider,
132+
ttsEngineProvider = NullTtsEngineProvider
131133
)?.let { SelectNavigatorItem.ReadAloud(it) }
132134

133135
val factories = listOfNotNull(

demos/navigator/src/main/java/org/readium/demo/navigator/reader/ReaderOpener.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.readium.navigator.common.PreferencesEditor
2727
import org.readium.navigator.common.Settings
2828
import org.readium.navigator.common.SettingsController
2929
import org.readium.navigator.media.readaloud.ReadAloudNavigatorFactory
30+
import org.readium.navigator.media.readaloud.ReadAloudSettings
3031
import org.readium.navigator.web.fixedlayout.FixedWebGoLocation
3132
import org.readium.navigator.web.fixedlayout.FixedWebLocation
3233
import org.readium.navigator.web.fixedlayout.FixedWebRenditionController
@@ -40,10 +41,12 @@ import org.readium.navigator.web.reflowable.ReflowableWebRenditionFactory
4041
import org.readium.navigator.web.reflowable.ReflowableWebSelectionLocation
4142
import org.readium.navigator.web.reflowable.preferences.ReflowableWebPreferences
4243
import org.readium.r2.shared.ExperimentalReadiumApi
44+
import org.readium.r2.shared.guided.GuidedNavigationRole
4345
import org.readium.r2.shared.publication.Locator
4446
import org.readium.r2.shared.publication.Publication
4547
import org.readium.r2.shared.util.AbsoluteUrl
4648
import org.readium.r2.shared.util.Error
49+
import org.readium.r2.shared.util.Language
4750
import org.readium.r2.shared.util.Try
4851
import org.readium.r2.shared.util.getOrElse
4952

@@ -197,7 +200,18 @@ class ReaderOpener(
197200
navigatorFactory: ReadAloudNavigatorFactory,
198201
initialLocator: Locator?,
199202
): Try<ReadAloudReaderState, Error> {
200-
val navigator = navigatorFactory.createNavigator()
203+
val initialSettings = ReadAloudSettings(
204+
language = Language("en"),
205+
overrideContentLanguage = true,
206+
preferRecordedVoices = true,
207+
pitch = 1.0,
208+
speed = 1.0,
209+
voices = emptyMap(),
210+
escapableRoles = GuidedNavigationRole.ESCAPABLE_ROLES.toSet(),
211+
skippableRoles = GuidedNavigationRole.SKIPPABLE_ROLES.toSet()
212+
)
213+
214+
val navigator = navigatorFactory.createNavigator(initialSettings)
201215
.getOrElse { return Try.failure(it) }
202216

203217
val readerState = ReadAloudReaderState(

readium/adapters/exoplayer/readaloud/src/main/java/org/readium/adapter/exoplayer/readaloud/ExoPlayerEngine.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ public class ExoPlayerEngine private constructor(
5959
.setHandleAudioBecomingNoisy(true)
6060
.build()
6161

62+
exoPlayer.preloadConfiguration = ExoPlayer.PreloadConfiguration(10_000_000L)
63+
6264
return ExoPlayerEngine(exoPlayer, listener)
6365
}
6466
}
@@ -76,11 +78,11 @@ public class ExoPlayerEngine private constructor(
7678
Player.STATE_ENDED -> AudioEngine.State.Ended
7779
else -> null
7880
}
79-
newState?.let { this@ExoPlayerEngine.listener.onStateChanged(it) }
81+
newState?.let { this@ExoPlayerEngine.listener.onStateChanged(this@ExoPlayerEngine, it) }
8082
}
8183

8284
if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
83-
this@ExoPlayerEngine.listener.onItemChanged(player.currentMediaItemIndex)
85+
this@ExoPlayerEngine.listener.onItemChanged(this@ExoPlayerEngine, player.currentMediaItemIndex)
8486
}
8587
}
8688
}
@@ -128,6 +130,10 @@ public class ExoPlayerEngine private constructor(
128130
}
129131
}
130132

133+
override fun seekTo(index: Int) {
134+
exoPlayer.seekTo(index, 0L)
135+
}
136+
131137
override var playWhenReady: Boolean
132138
get() = exoPlayer.playWhenReady
133139
set(value) {

readium/navigators/media/readaloud/src/main/java/org/readium/navigator/media/readaloud/AudioEngine.kt

Lines changed: 0 additions & 47 deletions
This file was deleted.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2025 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
@file:OptIn(ExperimentalReadiumApi::class)
8+
9+
package org.readium.navigator.media.readaloud
10+
11+
import org.readium.r2.shared.ExperimentalReadiumApi
12+
import org.readium.r2.shared.publication.services.GuidedNavigationIterator
13+
import org.readium.r2.shared.publication.services.GuidedNavigationService
14+
15+
internal class ContentIteratorGuidedNavigationService : GuidedNavigationService {
16+
17+
override fun iterator(): GuidedNavigationIterator {
18+
TODO("Not yet implemented")
19+
}
20+
}

readium/navigators/media/readaloud/src/main/java/org/readium/navigator/media/readaloud/GuidedNavigationAdapter.kt

Lines changed: 0 additions & 61 deletions
This file was deleted.

readium/navigators/media/readaloud/src/main/java/org/readium/navigator/media/readaloud/PlaybackEngine.kt

Lines changed: 0 additions & 52 deletions
This file was deleted.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2025 Readium Foundation. All rights reserved.
3+
* Use of this source code is governed by the BSD-style license
4+
* available in the top-level LICENSE file of the project.
5+
*/
6+
7+
@file:OptIn(ExperimentalReadiumApi::class)
8+
9+
package org.readium.navigator.media.readaloud
10+
11+
import kotlin.properties.Delegates
12+
import org.readium.r2.shared.ExperimentalReadiumApi
13+
14+
internal class ReadAloudDataLoader(
15+
private val segmentFactory: ReadAloudSegmentFactory,
16+
initialSettings: ReadAloudSettings,
17+
) {
18+
19+
sealed interface NodeInfo
20+
21+
data class ItemRef(
22+
val segment: ReadAloudSegment,
23+
val nodeIndex: Int?,
24+
) : NodeInfo
25+
26+
data object EmptyNode : NodeInfo
27+
28+
var settings by Delegates.observable(initialSettings) { property, oldValue, newValue ->
29+
preloadedRefs.clear()
30+
}
31+
32+
private val preloadedRefs: MutableMap<ReadAloudNode, ItemRef> = mutableMapOf()
33+
34+
fun onPlaybackProgressed(node: ReadAloudNode) {
35+
val nextNode = node.next() ?: return
36+
37+
if (nextNode !in preloadedRefs) {
38+
loadSegmentForNode(nextNode)
39+
}
40+
}
41+
42+
fun getItemRef(node: ReadAloudNode): ItemRef? {
43+
loadSegmentForNode(node)
44+
return preloadedRefs[node]
45+
}
46+
47+
private fun loadSegmentForNode(node: ReadAloudNode) {
48+
if (node in preloadedRefs) {
49+
return
50+
}
51+
52+
val segment = segmentFactory.createSegmentFromNode(node)
53+
?: return // Ended
54+
55+
val refs = computeRefsForSegment(segment)
56+
preloadedRefs.putAll(refs)
57+
}
58+
59+
private fun computeRefsForSegment(segment: ReadAloudSegment): Map<ReadAloudNode, ItemRef> {
60+
val plainRefs = segment.nodes
61+
.withIndex()
62+
.associate { (index, node) -> node to ItemRef(segment, index) }
63+
64+
val emptyRefs = segment.emptyNodes.associateWith { ItemRef(segment, null) }
65+
66+
return plainRefs + emptyRefs
67+
}
68+
}

0 commit comments

Comments
 (0)