Skip to content

Commit

Permalink
Fix voiceLanguage NPE and add tests for NavigationSpeechPlayer
Browse files Browse the repository at this point in the history
  • Loading branch information
danesfeder committed Jun 25, 2018
1 parent 9f9c37e commit 9fb9d47
Show file tree
Hide file tree
Showing 18 changed files with 621 additions and 239 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import android.location.Location;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.mapbox.api.directions.v5.DirectionsCriteria;
Expand All @@ -22,7 +23,9 @@
import com.mapbox.services.android.navigation.ui.v5.route.ViewRouteFetcher;
import com.mapbox.services.android.navigation.ui.v5.route.ViewRouteListener;
import com.mapbox.services.android.navigation.ui.v5.summary.SummaryModel;
import com.mapbox.services.android.navigation.ui.v5.voice.NavigationInstructionPlayer;
import com.mapbox.services.android.navigation.ui.v5.voice.NavigationSpeechPlayer;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechAnnouncement;
import com.mapbox.services.android.navigation.ui.v5.voice.SpeechPlayerProvider;
import com.mapbox.services.android.navigation.v5.milestone.BannerInstructionMilestone;
import com.mapbox.services.android.navigation.v5.milestone.Milestone;
import com.mapbox.services.android.navigation.v5.milestone.MilestoneEventListener;
Expand Down Expand Up @@ -58,7 +61,7 @@ public class NavigationViewModel extends AndroidViewModel {
private ViewRouteFetcher navigationViewRouteEngine;
private LocationEngineConductor locationEngineConductor;
private NavigationViewEventDispatcher navigationViewEventDispatcher;
private NavigationInstructionPlayer instructionPlayer;
private NavigationSpeechPlayer instructionPlayer;
private ConnectivityManager connectivityManager;
private RouteProgress routeProgress;
private String feedbackId;
Expand Down Expand Up @@ -212,9 +215,19 @@ private void initUnitType(NavigationUiOptions options) {
unitType = options.directionsRoute().routeOptions().voiceUnits();
}

private void initTimeFormat(MapboxNavigationOptions options) {
timeFormatType = options.timeFormatType();
}

private void initVoiceInstructions(NavigationViewOptions options) {
boolean languageSupported = options.directionsRoute().voiceLanguage() != null;
instructionPlayer = new NavigationInstructionPlayer(getApplication(), language, languageSupported, accessToken);
boolean voiceLanguageSupported = options.directionsRoute().voiceLanguage() != null;
SpeechPlayerProvider speechPlayerProvider = initializeSpeechPlayerProvider(voiceLanguageSupported);
instructionPlayer = new NavigationSpeechPlayer(speechPlayerProvider);
}

@NonNull
private SpeechPlayerProvider initializeSpeechPlayerProvider(boolean voiceLanguageSupported) {
return new SpeechPlayerProvider(getApplication(), language, voiceLanguageSupported, accessToken);
}

private void initNavigation(Context context, MapboxNavigationOptions options) {
Expand All @@ -238,10 +251,6 @@ private void addMilestones(NavigationViewOptions options) {
}
}

private void initTimeFormat(MapboxNavigationOptions options) {
timeFormatType = options.timeFormatType();
}

private ProgressChangeListener progressChangeListener = new ProgressChangeListener() {
@Override
public void onProgressChange(Location location, RouteProgress routeProgress) {
Expand All @@ -267,7 +276,11 @@ public void userOffRoute(Location location) {
@Override
public void onMilestoneEvent(RouteProgress routeProgress, String instruction, Milestone milestone) {
if (milestone instanceof VoiceInstructionMilestone) {
instructionPlayer.play((VoiceInstructionMilestone) milestone);
SpeechAnnouncement speechAnnouncement = SpeechAnnouncement.builder()
.announcement(((VoiceInstructionMilestone) milestone).getAnnouncement())
.ssmlAnnouncememnt(((VoiceInstructionMilestone) milestone).getSsmlAnnouncement())
.build();
instructionPlayer.play(speechAnnouncement);
}
updateBannerInstruction(routeProgress, milestone);
sendEventArrival(routeProgress, milestone);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,28 @@
*
* @since 0.6.0
*/
class AndroidSpeechPlayer implements InstructionPlayer {
class AndroidSpeechPlayer implements SpeechPlayer {

private static final String DEFAULT_UTTERANCE_ID = "default_id";

private TextToSpeech textToSpeech;
private SpeechListener speechListener;

private boolean isMuted;
private boolean languageSupported = false;
private InstructionListener instructionListener;

/**
* Creates an instance of {@link AndroidSpeechPlayer}.
*
* @param context used to create an instance of {@link TextToSpeech}
* @param context used to create an instance of {@link TextToSpeech}
* @param language to initialize locale to set
* @since 0.6.0
*/
AndroidSpeechPlayer(Context context, final String language, final InstructionListener instructionListener) {
AndroidSpeechPlayer(Context context, final String language, final SpeechListener speechListener) {
textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
setInstructionListener(instructionListener);
setSpeechListener(speechListener);

boolean ableToInitialize = status != TextToSpeech.ERROR && language != null;
if (!ableToInitialize) {
Expand All @@ -52,10 +53,14 @@ public void onInit(int status) {
/**
* Plays the given voice instruction using TTS
*
* @param instruction voice instruction to be synthesized and played
* @param speechAnnouncement with voice instruction to be synthesized and played
*/
@Override
public void play(String instruction) {
public void play(SpeechAnnouncement speechAnnouncement) {
if (speechAnnouncement == null) {
return;
}
String instruction = speechAnnouncement.announcement();
boolean canPlay = languageSupported && !isMuted && !TextUtils.isEmpty(instruction);
if (!canPlay) {
return;
Expand Down Expand Up @@ -129,17 +134,17 @@ private void initializeWithLanguage(Locale language) {

private void fireInstructionListenerIfApi14() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
instructionListener.onStart();
speechListener.onStart();
}
}

private void setInstructionListener(final InstructionListener instructionListener) {
this.instructionListener = instructionListener;
private void setSpeechListener(final SpeechListener speechListener) {
this.speechListener = speechListener;

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
textToSpeech.setOnUtteranceCompletedListener(new Api14UtteranceListener(instructionListener));
textToSpeech.setOnUtteranceCompletedListener(new Api14UtteranceListener(speechListener));
} else {
textToSpeech.setOnUtteranceProgressListener(new UtteranceListener(instructionListener));
textToSpeech.setOnUtteranceProgressListener(new UtteranceListener(speechListener));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
import android.speech.tts.TextToSpeech;

class Api14UtteranceListener implements TextToSpeech.OnUtteranceCompletedListener {
private InstructionListener instructionListener;
private SpeechListener speechListener;

Api14UtteranceListener(InstructionListener instructionListener) {
this.instructionListener = instructionListener;
Api14UtteranceListener(SpeechListener speechListener) {
this.speechListener = speechListener;
}

@Override
public void onUtteranceCompleted(String utteranceId) {
instructionListener.onDone();
speechListener.onDone();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,30 +25,34 @@
* Will retrieve synthesized speech mp3s from Mapbox's API Voice.
* </p>
*/
class MapboxSpeechPlayer implements InstructionPlayer {
class MapboxSpeechPlayer implements SpeechPlayer {

private static final long TEN_MEGABYTE_CACHE_SIZE = 10 * 1098 * 1098;
private static final String OKHTTP_INSTRUCTION_CACHE = "okhttp_instruction_cache";
private static final String MAPBOX_INSTRUCTION_CACHE = "mapbox_instruction_cache";
private static final String SSML_TEXT_TYPE = "ssml";
private static final String TEXT_TYPE = "text";
private static final String ERROR_TEXT = "Unable to set data source for the media mediaPlayer! %s";
private Queue<File> instructionQueue;

private VoiceInstructionLoader voiceInstructionLoader;
private SpeechAnnouncement announcement;
private SpeechListener speechListener;
private MediaPlayer mediaPlayer;
private InstructionListener instructionListener;
private boolean isMuted;
private Queue<File> instructionQueue;
private File mapboxCache;
private Cache okhttpCache;
private boolean isMuted;

/**
* Construct an instance of {@link MapboxSpeechPlayer}
*
* @param context to setup the caches
* @param language for which language
* @param context to setup the caches
* @param language for which language
* @param accessToken a valid Mapbox access token
*/
MapboxSpeechPlayer(Context context, String language, @NonNull InstructionListener instructionListener,
MapboxSpeechPlayer(Context context, String language, @NonNull SpeechListener speechListener,
String accessToken) {
this.instructionListener = instructionListener;
this.speechListener = speechListener;
setupCaches(context);
instructionQueue = new ConcurrentLinkedQueue();
voiceInstructionLoader = VoiceInstructionLoader.builder()
Expand All @@ -69,18 +73,24 @@ private void setupCaches(Context context) {
/**
* Plays the specified text instruction using MapboxSpeech API, defaulting to SSML input type
*
* @param instruction voice instruction to be synthesized and played
* @param speechAnnouncement with voice instruction to be synthesized and played
*/
@Override
public void play(String instruction) {
play(instruction, SSML_TEXT_TYPE);
public void play(SpeechAnnouncement speechAnnouncement) {
this.announcement = speechAnnouncement;
String ssmlAnnouncement = announcement.ssmlAnnouncememnt();
if (ssmlAnnouncement != null) {
play(ssmlAnnouncement, SSML_TEXT_TYPE);
} else {
play(announcement.announcement(), TEXT_TYPE);
}
}

/**
* Plays the specified text instruction using MapboxSpeech API
*
* @param instruction voice instruction to be synthesized and played
* @param textType either "ssml" or "text"
* @param textType either "ssml" or "text"
*/
private void play(String instruction, String textType) {
downloadVoiceFile(instruction, textType);
Expand Down Expand Up @@ -113,7 +123,7 @@ private void flushCache() {
try {
okhttpCache.flush();
} catch (IOException exception) {
Timber.e(exception.getMessage());
Timber.e(exception);
}
}

Expand All @@ -129,12 +139,12 @@ private void stopMediaPlayerPlaying() {
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.release();
if (instructionListener != null) {
instructionListener.onDone();
if (speechListener != null) {
speechListener.onDone();
}
}
} catch (IllegalStateException exception) {
Timber.e(exception.getMessage());
Timber.e(exception);
}
}

Expand Down Expand Up @@ -166,7 +176,7 @@ public void onFailure(Call<ResponseBody> call, Throwable throwable) {
}

private void onError(String errorText) {
instructionListener.onError(true, errorText);
speechListener.onError(errorText, announcement);
}

private void playInstruction(@NonNull File instruction) {
Expand All @@ -190,7 +200,7 @@ private void pauseInstruction() {
mediaPlayer.stop();
}
} catch (IllegalStateException exception) {
Timber.e(exception.getMessage());
Timber.e(exception);
}
}

Expand All @@ -206,8 +216,8 @@ private void setListeners() {
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
if (instructionListener != null) {
instructionListener.onStart();
if (speechListener != null) {
speechListener.onStart();
}
mp.start();
}
Expand All @@ -216,8 +226,8 @@ public void onPrepared(MediaPlayer mp) {
@Override
public void onCompletion(MediaPlayer mp) {
mp.release();
if (instructionListener != null) {
instructionListener.onDone();
if (speechListener != null) {
speechListener.onDone();
}
onInstructionFinishedPlaying();
}
Expand All @@ -243,12 +253,12 @@ private void startNextInstruction() {

private void clearInstructionUrls() {
while (!instructionQueue.isEmpty()) {
instructionQueue.remove().delete();
instructionQueue.poll();
}
}

private void executeInstructionTask(ResponseBody responseBody) {
new InstructionDownloadTask(mapboxCache.getPath(), new InstructionDownloadTask.TaskListener() {
new SpeechDownloadTask(mapboxCache.getPath(), new SpeechDownloadTask.TaskListener() {
@Override
public void onFinishedDownloading(@NonNull File instructionFile) {
playInstructionIfUpNext(instructionFile);
Expand All @@ -257,7 +267,7 @@ public void onFinishedDownloading(@NonNull File instructionFile) {

@Override
public void onErrorDownloading() {
if (instructionListener != null) {
if (speechListener != null) {
onError("There was an error downloading the voice files.");
}
}
Expand Down
Loading

0 comments on commit 9fb9d47

Please sign in to comment.