Skip to content

Commit

Permalink
Optimize opening an EPUB
Browse files Browse the repository at this point in the history
Don't check for a non-empty content iterator when creating the TTS navigator factory
  • Loading branch information
mickael-menu committed Oct 5, 2023
1 parent fdfbfd3 commit 9d5033c
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public class TtsNavigator<S : TtsEngine.Settings, P : TtsEngine.Preferences<P>,

val contentIterator =
TtsUtteranceIterator(publication, tokenizerFactory, initialLocator)
if (!contentIterator.hasNext()) {
return null
}

val ttsEngine =
ttsEngineProvider.createEngine(publication, actualInitialPreferences)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import org.readium.r2.shared.ExperimentalReadiumApi
import org.readium.r2.shared.publication.Locator
import org.readium.r2.shared.publication.Publication
import org.readium.r2.shared.publication.services.content.Content
import org.readium.r2.shared.publication.services.content.content
import org.readium.r2.shared.util.Language
import org.readium.r2.shared.util.tokenizer.DefaultTextContentTokenizer
import org.readium.r2.shared.util.tokenizer.TextTokenizer
Expand All @@ -34,14 +33,14 @@ public class TtsNavigatorFactory<S : TtsEngine.Settings, P : TtsEngine.Preferenc
) {
public companion object {

public suspend operator fun invoke(
public operator fun invoke(
application: Application,
publication: Publication,
tokenizerFactory: (language: Language?) -> TextTokenizer = defaultTokenizerFactory,
metadataProvider: MediaMetadataProvider = defaultMediaMetadataProvider,
defaults: AndroidTtsDefaults = AndroidTtsDefaults(),
voiceSelector: (Language?, Set<AndroidTtsEngine.Voice>) -> AndroidTtsEngine.Voice? = defaultVoiceSelector
): AndroidTtsNavigatorFactory? {
): AndroidTtsNavigatorFactory {
val engineProvider = AndroidTtsEngineProvider(
context = application,
defaults = defaults,
Expand All @@ -57,44 +56,37 @@ public class TtsNavigatorFactory<S : TtsEngine.Settings, P : TtsEngine.Preferenc
)
}

public suspend operator fun <S : TtsEngine.Settings, P : TtsEngine.Preferences<P>, E : PreferencesEditor<P>,
public operator fun <S : TtsEngine.Settings, P : TtsEngine.Preferences<P>, E : PreferencesEditor<P>,
F : TtsEngine.Error, V : TtsEngine.Voice> invoke(
application: Application,
publication: Publication,
ttsEngineProvider: TtsEngineProvider<S, P, E, F, V>,
tokenizerFactory: (language: Language?) -> TextTokenizer = defaultTokenizerFactory,
metadataProvider: MediaMetadataProvider = defaultMediaMetadataProvider
): TtsNavigatorFactory<S, P, E, F, V>? {
return createNavigatorFactory(
): TtsNavigatorFactory<S, P, E, F, V> =
createNavigatorFactory(
application,
publication,
ttsEngineProvider,
tokenizerFactory,
metadataProvider
)
}

private suspend fun <S : TtsEngine.Settings, P : TtsEngine.Preferences<P>, E : PreferencesEditor<P>,
private fun <S : TtsEngine.Settings, P : TtsEngine.Preferences<P>, E : PreferencesEditor<P>,
F : TtsEngine.Error, V : TtsEngine.Voice> createNavigatorFactory(
application: Application,
publication: Publication,
ttsEngineProvider: TtsEngineProvider<S, P, E, F, V>,
tokenizerFactory: (language: Language?) -> TextTokenizer,
metadataProvider: MediaMetadataProvider
): TtsNavigatorFactory<S, P, E, F, V>? {
publication.content()
?.iterator()
?.takeIf { it.hasNext() }
?: return null

return TtsNavigatorFactory(
): TtsNavigatorFactory<S, P, E, F, V> =
TtsNavigatorFactory(
application,
publication,
ttsEngineProvider,
tokenizerFactory,
metadataProvider
)
}

/**
* The default content tokenizer will split the [Content.Element] items into individual sentences.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,19 @@ internal class TtsUtteranceIterator(
private fun createIterator(locator: Locator?): Content.Iterator =
contentService.content(locator).iterator()

suspend fun hasPrevious(): Boolean =
hasNextIn(Direction.Backward)

suspend fun hasNext(): Boolean =
hasNextIn(Direction.Forward)

private suspend fun hasNextIn(direction: Direction): Boolean {
if (utterances.isEmpty()) {
loadNextUtterances(direction)
}
return utterances.hasNextIn(direction)
}

/**
* Advances to the previous item and returns it, or null if we reached the beginning.
*/
Expand Down Expand Up @@ -217,6 +230,12 @@ internal class TtsUtteranceIterator(
}
}

private fun <E> CursorList<E>.hasNextIn(direction: Direction): Boolean =
when (direction) {
Direction.Forward -> hasNext()
Direction.Backward -> hasPrevious()
}

private fun <E> CursorList<E>.nextIn(direction: Direction): E? =
when (direction) {
Direction.Forward -> if (hasNext()) next() else null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,14 @@ sealed class VisualReaderInitData(
override val bookId: Long,
override val publication: Publication,
val initialLocation: Locator?,
val ttsInitData: TtsInitData?
val ttsInitData: TtsInitData
) : ReaderInitData()

class ImageReaderInitData(
bookId: Long,
publication: Publication,
initialLocation: Locator?,
ttsInitData: TtsInitData?
ttsInitData: TtsInitData
) : VisualReaderInitData(bookId, publication, initialLocation, ttsInitData)

class EpubReaderInitData(
Expand All @@ -46,7 +46,7 @@ class EpubReaderInitData(
initialLocation: Locator?,
val preferencesManager: PreferencesManager<EpubPreferences>,
val navigatorFactory: EpubNavigatorFactory,
ttsInitData: TtsInitData?
ttsInitData: TtsInitData
) : VisualReaderInitData(bookId, publication, initialLocation, ttsInitData)

class PdfReaderInitData(
Expand All @@ -55,7 +55,7 @@ class PdfReaderInitData(
initialLocation: Locator?,
val preferencesManager: PreferencesManager<PdfiumPreferences>,
val navigatorFactory: PdfiumNavigatorFactory,
ttsInitData: TtsInitData?
ttsInitData: TtsInitData
) : VisualReaderInitData(bookId, publication, initialLocation, ttsInitData)

class TtsInitData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,13 +235,13 @@ class ReaderRepository(
private suspend fun getTtsInitData(
bookId: Long,
publication: Publication
): TtsInitData? {
): TtsInitData {
val preferencesManager = AndroidTtsPreferencesManagerFactory(preferencesDataStore)
.createPreferenceManager(bookId)
val navigatorFactory = TtsNavigatorFactory(
application,
publication
) ?: return null
)
return TtsInitData(mediaServiceFacade, navigatorFactory, preferencesManager)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class TtsViewModel private constructor(
viewModelScope: CoroutineScope,
readerInitData: ReaderInitData
): TtsViewModel? {
if (readerInitData !is VisualReaderInitData || readerInitData.ttsInitData == null) {
if (readerInitData !is VisualReaderInitData) {
return null
}

Expand Down
2 changes: 1 addition & 1 deletion test-app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@
<string name="play_or_pause">Play or pause</string>
<string name="go_forward_30_seconds">Go forward 30 seconds</string>

<string name="tts_error_initialization">Failed to initialize the TTS engine</string>
<string name="tts_error_initialization">This publication cannot be read aloud</string>
<string name="tts_error_language_support_incomplete">The language %s requires additional data. Do you want to download it?</string>
<string name="tts_error_network">A networking error occurred</string>
<string name="tts_error_other">A TTS error occurred</string>
Expand Down

0 comments on commit 9d5033c

Please sign in to comment.