Skip to content

Commit

Permalink
refactor voice module to use mapbox navigation consumer generic callback
Browse files Browse the repository at this point in the history
  • Loading branch information
Guardiola31337 committed Mar 16, 2021
1 parent adbe1e4 commit eda0a6e
Show file tree
Hide file tree
Showing 12 changed files with 150 additions and 129 deletions.
17 changes: 6 additions & 11 deletions libnavui-voice/api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,18 @@ package com.mapbox.navigation.ui.voice.api {
ctor public MapboxSpeechApi(android.content.Context context, String accessToken, String language);
method public void cancel();
method public void clean(com.mapbox.navigation.ui.voice.model.SpeechAnnouncement announcement);
method public void generate(com.mapbox.api.directions.v5.models.VoiceInstructions voiceInstruction, com.mapbox.navigation.ui.voice.api.SpeechCallback callback);
method public void generate(com.mapbox.api.directions.v5.models.VoiceInstructions voiceInstruction, com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer<com.mapbox.navigation.ui.base.model.Expected<com.mapbox.navigation.ui.voice.model.SpeechValue,com.mapbox.navigation.ui.voice.model.SpeechError>> consumer);
}

@UiThread public final class MapboxVoiceInstructionsPlayer {
ctor public MapboxVoiceInstructionsPlayer(android.content.Context context, String accessToken, String language, com.mapbox.navigation.ui.voice.options.VoiceInstructionsPlayerOptions options = VoiceInstructionsPlayerOptions.<init>().build());
ctor public MapboxVoiceInstructionsPlayer(android.content.Context context, String accessToken, String language);
method public void clear();
method public void play(com.mapbox.navigation.ui.voice.model.SpeechAnnouncement announcement, com.mapbox.navigation.ui.voice.api.VoiceInstructionsPlayerCallback callback);
method public void play(com.mapbox.navigation.ui.voice.model.SpeechAnnouncement announcement, com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer<com.mapbox.navigation.ui.voice.model.SpeechAnnouncement> consumer);
method public void shutdown();
method public void volume(com.mapbox.navigation.ui.voice.model.SpeechVolume state);
}

public interface SpeechCallback {
method public void onSpeech(com.mapbox.navigation.ui.base.model.Expected<com.mapbox.navigation.ui.voice.model.SpeechValue,com.mapbox.navigation.ui.voice.model.SpeechError> state);
}

public interface VoiceInstructionsPlayerCallback {
method public void onDone(com.mapbox.navigation.ui.voice.model.SpeechAnnouncement announcement);
}

}

package com.mapbox.navigation.ui.voice.model {
Expand Down Expand Up @@ -62,13 +54,16 @@ package com.mapbox.navigation.ui.voice.model {
}

public final class SpeechVolume {
ctor public SpeechVolume(@FloatRange(from=0.0, to=1.0) float level);
ctor public SpeechVolume(@FloatRange(from=com.mapbox.navigation.ui.voice.model.SpeechVolumeKt.MINIMUM_VOLUME_LEVEL, to=com.mapbox.navigation.ui.voice.model.SpeechVolumeKt.MAXIMUM_VOLUME_LEVEL) float level);
method public float component1();
method public com.mapbox.navigation.ui.voice.model.SpeechVolume copy(float level);
method public float getLevel();
property public final float level;
}

public final class SpeechVolumeKt {
}

}

package com.mapbox.navigation.ui.voice.options {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.mapbox.navigation.ui.voice.api
import android.content.Context
import com.mapbox.api.directions.v5.models.VoiceInstructions
import com.mapbox.navigation.ui.base.model.Expected
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
import com.mapbox.navigation.ui.voice.VoiceAction
import com.mapbox.navigation.ui.voice.VoiceProcessor
import com.mapbox.navigation.ui.voice.VoiceResult
Expand Down Expand Up @@ -44,13 +45,18 @@ class MapboxSpeechApi @JvmOverloads constructor(
* voice instruction [SpeechAnnouncement] including the synthesized speech mp3 file
* from Mapbox's API Voice.
* @param voiceInstruction VoiceInstructions object representing [VoiceInstructions]
* @param callback SpeechCallback
* @param consumer is a [SpeechValue] including the announcement to be played when the
* announcement is ready or a [SpeechError] including the error information and a fallback
* with the raw announcement (without file) that can be played with a text-to-speech engine.
* @see [cancel]
*/
fun generate(voiceInstruction: VoiceInstructions, callback: SpeechCallback) {
fun generate(
voiceInstruction: VoiceInstructions,
consumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>>
) {
currentVoiceFileJob?.cancel()
currentVoiceFileJob = mainJobController.scope.launch {
retrieveVoiceFile(voiceInstruction, callback)
retrieveVoiceFile(voiceInstruction, consumer)
}
}

Expand All @@ -73,7 +79,7 @@ class MapboxSpeechApi @JvmOverloads constructor(

private suspend fun retrieveVoiceFile(
voiceInstruction: VoiceInstructions,
callback: SpeechCallback
consumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>>
) {
when (val result = voiceAPI.retrieveVoiceFile(voiceInstruction)) {
is VoiceState.VoiceResponse -> {
Expand All @@ -84,7 +90,7 @@ class MapboxSpeechApi @JvmOverloads constructor(
is VoiceState.VoiceFile -> {
val announcement = voiceInstruction.announcement()
val ssmlAnnouncement = voiceInstruction.ssmlAnnouncement()
callback.onSpeech(
consumer.accept(
Expected.Success(
SpeechValue(
// Can't be null as it's checked in retrieveVoiceFile
Expand All @@ -100,7 +106,7 @@ class MapboxSpeechApi @JvmOverloads constructor(
processVoiceAnnouncement(
voiceInstruction
) { available ->
callback.onSpeech(
consumer.accept(
Expected.Failure(
SpeechError(
result.exception,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.mapbox.navigation.ui.voice.api
import android.content.Context
import android.media.AudioManager
import androidx.annotation.UiThread
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement
import com.mapbox.navigation.ui.voice.model.SpeechVolume
import com.mapbox.navigation.ui.voice.options.VoiceInstructionsPlayerOptions
Expand Down Expand Up @@ -41,8 +42,8 @@ class MapboxVoiceInstructionsPlayer @JvmOverloads constructor(
audioFocusDelegate.abandonFocus()
val currentPlayCallback = playCallbackQueue.poll()
val currentAnnouncement = currentPlayCallback.announcement
val currentClientCallback = currentPlayCallback.callback
currentClientCallback.onDone(currentAnnouncement)
val currentClientCallback = currentPlayCallback.consumer
currentClientCallback.accept(currentAnnouncement)
play()
}
}
Expand All @@ -58,13 +59,13 @@ class MapboxVoiceInstructionsPlayer @JvmOverloads constructor(
* the given voice instruction will be queued to play after.
* @param announcement object including the announcement text
* and optionally a synthesized speech mp3.
* @param callback
* @param consumer represents that the speech player is done playing
*/
fun play(
announcement: SpeechAnnouncement,
callback: VoiceInstructionsPlayerCallback
consumer: MapboxNavigationConsumer<SpeechAnnouncement>
) {
playCallbackQueue.add(PlayCallback(announcement, callback))
playCallbackQueue.add(PlayCallback(announcement, consumer))
if (playCallbackQueue.size == 1) {
play()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.mapbox.navigation.ui.voice.api

import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement

internal data class PlayCallback(
val announcement: SpeechAnnouncement,
val callback: VoiceInstructionsPlayerCallback
val consumer: MapboxNavigationConsumer<SpeechAnnouncement>
)

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement
/**
* Interface definition for a callback to be invoked when a voice instruction is played.
*/
interface VoiceInstructionsPlayerCallback {
internal interface VoiceInstructionsPlayerCallback {

/**
* Invoked when the speech player is done playing.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@ package com.mapbox.navigation.ui.voice.model

import androidx.annotation.FloatRange

private const val MINIMUM_VOLUME_LEVEL = 0.0
private const val MAXIMUM_VOLUME_LEVEL = 1.0

/**
* The state is returned if we change the speech volume.
* @param level
* @param level volume level must be a value between [0.0..1.0]
*/
data class SpeechVolume(
@FloatRange(from = 0.0, to = 1.0)
@FloatRange(from = MINIMUM_VOLUME_LEVEL, to = MAXIMUM_VOLUME_LEVEL)
val level: Float
)
) {
init {
require(level in MINIMUM_VOLUME_LEVEL..MAXIMUM_VOLUME_LEVEL) {
"Volume level must be between [$MINIMUM_VOLUME_LEVEL..$MAXIMUM_VOLUME_LEVEL]"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.Context
import com.mapbox.api.directions.v5.models.VoiceInstructions
import com.mapbox.navigation.testing.MainCoroutineRule
import com.mapbox.navigation.ui.base.model.Expected
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
import com.mapbox.navigation.ui.voice.VoiceAction
import com.mapbox.navigation.ui.voice.VoiceProcessor
import com.mapbox.navigation.ui.voice.VoiceResult
Expand Down Expand Up @@ -85,9 +86,9 @@ class MapboxSpeechApiTest {
""".trimIndent()
every { mockedVoiceInstructions.announcement() } returns anAnnouncement
every { mockedVoiceInstructions.ssmlAnnouncement() } returns aSsmlAnnouncement
val speechCallback: SpeechCallback = mockk()
val speechConsumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>> = mockk()
val speechValueSlot = slot<Expected.Success<SpeechValue>>()
every { speechCallback.onSpeech(capture(speechValueSlot)) } just Runs
every { speechConsumer.accept(capture(speechValueSlot)) } just Runs
val mockedInstructionFile: File = mockk()
val mockedVoiceApi: MapboxVoiceApi = mockk()
coEvery {
Expand All @@ -104,10 +105,10 @@ class MapboxSpeechApiTest {
} returns mockedVoiceApi
val mapboxSpeechApi = MapboxSpeechApi(aMockedContext, anyAccessToken, anyLanguage)

mapboxSpeechApi.generate(mockedVoiceInstructions, speechCallback)
mapboxSpeechApi.generate(mockedVoiceInstructions, speechConsumer)

verify(exactly = 1) {
speechCallback.onSpeech(
speechConsumer.accept(
speechValueSlot.captured
)
}
Expand All @@ -129,8 +130,8 @@ class MapboxSpeechApiTest {
""".trimIndent()
every { mockedVoiceInstructions.announcement() } returns anAnnouncement
every { mockedVoiceInstructions.ssmlAnnouncement() } returns aSsmlAnnouncement
val speechCallback: SpeechCallback = mockk()
every { speechCallback.onSpeech(any()) } just Runs
val speechConsumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>> = mockk()
every { speechConsumer.accept(any()) } just Runs
val mockedVoiceError: VoiceState.VoiceError = VoiceState.VoiceError(
"code: 204, error: No data available"
)
Expand All @@ -156,10 +157,10 @@ class MapboxSpeechApiTest {
VoiceProcessor.process(any<VoiceAction.PrepareTypeAndAnnouncement>())
} returns VoiceResult.VoiceTypeAndAnnouncement.Success(mockedTypeAndAnnouncement)

mapboxSpeechApi.generate(mockedVoiceInstructions, speechCallback)
mapboxSpeechApi.generate(mockedVoiceInstructions, speechConsumer)

verify(exactly = 1) {
speechCallback.onSpeech(
speechConsumer.accept(
capture(speechErrorSlot)
)
}
Expand Down Expand Up @@ -191,8 +192,8 @@ class MapboxSpeechApiTest {
""".trimIndent()
every { mockedVoiceInstructions.announcement() } returns anAnnouncement
every { mockedVoiceInstructions.ssmlAnnouncement() } returns aSsmlAnnouncement
val speechCallback: SpeechCallback = mockk()
every { speechCallback.onSpeech(any()) } just Runs
val speechConsumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>> = mockk()
every { speechConsumer.accept(any()) } just Runs
val mockedVoiceError: VoiceState.VoiceError = VoiceState.VoiceError(
"code: 204, error: No data available"
)
Expand All @@ -216,7 +217,7 @@ class MapboxSpeechApiTest {
VoiceProcessor.process(any<VoiceAction.PrepareTypeAndAnnouncement>())
} returns VoiceResult.VoiceTypeAndAnnouncement.Failure(voiceTypeAndAnnouncementError)

mapboxSpeechApi.generate(mockedVoiceInstructions, speechCallback)
mapboxSpeechApi.generate(mockedVoiceInstructions, speechConsumer)

assertTrue(exceptions[0] is java.lang.IllegalStateException)
assertEquals(
Expand All @@ -233,7 +234,8 @@ class MapboxSpeechApiTest {
val anyAccessToken = "pk.123"
val anyLanguage = Locale.US.language
val mockedVoiceInstructions: VoiceInstructions = mockk()
val speechCallback: SpeechCallback = mockk()
val speechConsumer: MapboxNavigationConsumer<Expected<SpeechValue, SpeechError>> =
mockk()
val mockedVoiceResponse: VoiceState.VoiceResponse = VoiceState.VoiceResponse(mockk())
val mockedVoiceApi: MapboxVoiceApi = mockk()
coEvery {
Expand All @@ -250,7 +252,7 @@ class MapboxSpeechApiTest {
} returns mockedVoiceApi
val mapboxSpeechApi = MapboxSpeechApi(aMockedContext, anyAccessToken, anyLanguage)

mapboxSpeechApi.generate(mockedVoiceInstructions, speechCallback)
mapboxSpeechApi.generate(mockedVoiceInstructions, speechConsumer)

assertTrue(exceptions[0] is java.lang.IllegalStateException)
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.mapbox.navigation.ui.voice.api

import android.content.Context
import android.media.AudioManager
import com.mapbox.navigation.ui.base.util.MapboxNavigationConsumer
import com.mapbox.navigation.ui.voice.model.SpeechAnnouncement
import com.mapbox.navigation.ui.voice.model.SpeechVolume
import com.mapbox.navigation.ui.voice.options.VoiceInstructionsPlayerOptions
Expand Down Expand Up @@ -87,10 +88,10 @@ class MapboxVoiceInstructionsPlayerTest {
mockedVoiceInstructionsPlayerOptions
)
val mockedPlay: SpeechAnnouncement = mockedAnnouncement
val voiceInstructionsPlayerCallback: VoiceInstructionsPlayerCallback = mockk()
every { voiceInstructionsPlayerCallback.onDone(any()) } just Runs
val voiceInstructionsPlayerConsumer: MapboxNavigationConsumer<SpeechAnnouncement> = mockk()
every { voiceInstructionsPlayerConsumer.accept(any()) } just Runs

mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerCallback)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerConsumer)

verify(exactly = 1) {
mockedFilePlayer.play(mockedPlay, any())
Expand All @@ -99,7 +100,7 @@ class MapboxVoiceInstructionsPlayerTest {
mockedTextPlayer.play(mockedPlay, any())
}
verify(exactly = 1) {
voiceInstructionsPlayerCallback.onDone(mockedPlay)
voiceInstructionsPlayerConsumer.accept(mockedPlay)
}
}

Expand Down Expand Up @@ -140,10 +141,10 @@ class MapboxVoiceInstructionsPlayerTest {
mockedVoiceInstructionsPlayerOptions
)
val mockedPlay: SpeechAnnouncement = mockedAnnouncement
val voiceInstructionsPlayerCallback: VoiceInstructionsPlayerCallback = mockk()
every { voiceInstructionsPlayerCallback.onDone(any()) } just Runs
val voiceInstructionsPlayerConsumer: MapboxNavigationConsumer<SpeechAnnouncement> = mockk()
every { voiceInstructionsPlayerConsumer.accept(any()) } just Runs

mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerCallback)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerConsumer)

verify(exactly = 1) {
mockedTextPlayer.play(mockedPlay, any())
Expand All @@ -152,7 +153,7 @@ class MapboxVoiceInstructionsPlayerTest {
mockedFilePlayer.play(mockedPlay, any())
}
verify(exactly = 1) {
voiceInstructionsPlayerCallback.onDone(mockedPlay)
voiceInstructionsPlayerConsumer.accept(mockedPlay)
}
}

Expand Down Expand Up @@ -199,17 +200,17 @@ class MapboxVoiceInstructionsPlayerTest {
mockedVoiceInstructionsPlayerOptions
)
val mockedPlay: SpeechAnnouncement = mockedAnnouncement
val voiceInstructionsPlayerCallback: VoiceInstructionsPlayerCallback = mockk()
every { voiceInstructionsPlayerCallback.onDone(any()) } just Runs
val voiceInstructionsPlayerConsumer: MapboxNavigationConsumer<SpeechAnnouncement> = mockk()
every { voiceInstructionsPlayerConsumer.accept(any()) } just Runs

mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerCallback)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerCallback)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerConsumer)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerConsumer)

verifyOrder {
mockedFilePlayer.play(mockedPlay, any())
voiceInstructionsPlayerCallback.onDone(mockedPlay)
voiceInstructionsPlayerConsumer.accept(mockedPlay)
mockedTextPlayer.play(mockedPlay, any())
voiceInstructionsPlayerCallback.onDone(mockedPlay)
voiceInstructionsPlayerConsumer.accept(mockedPlay)
}
}

Expand Down Expand Up @@ -413,10 +414,10 @@ class MapboxVoiceInstructionsPlayerTest {
mockedVoiceInstructionsPlayerOptions
)
val mockedPlay: SpeechAnnouncement = mockedAnnouncement
val voiceInstructionsPlayerCallback: VoiceInstructionsPlayerCallback = mockk()
every { voiceInstructionsPlayerCallback.onDone(any()) } just Runs
val voiceInstructionsPlayerConsumer: MapboxNavigationConsumer<SpeechAnnouncement> = mockk()
every { voiceInstructionsPlayerConsumer.accept(any()) } just Runs

mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerCallback)
mapboxVoiceInstructionsPlayer.play(mockedPlay, voiceInstructionsPlayerConsumer)

verify(exactly = 1) {
mockedAudioFocusDelegate.requestFocus()
Expand Down
Loading

0 comments on commit eda0a6e

Please sign in to comment.