package net.osmand.plus.voice;
import android.media.MediaPlayer;
import net.osmand.PlatformUtil;
import net.osmand.plus.ApplicationMode;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.routing.VoiceRouter;
import org.apache.commons.logging.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* That class represents command player.
* It gets commands from input, analyze what files should be played and play
* them using media player
*/
public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer implements MediaPlayer.OnCompletionListener {
private static final String CONFIG_FILE = "_config.p";
private static final int[] MEDIA_VOICE_VERSION = new int[] { 0 }; // MUST BE SORTED, list of supported versions
private static final Log log = PlatformUtil.getLog(MediaCommandPlayerImpl.class);
// playing media
private MediaPlayer mediaPlayer;
// indicates that player is ready to play first file
private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
private VoiceRouter vrt;
public MediaCommandPlayerImpl(OsmandApplication ctx, ApplicationMode applicationMode, VoiceRouter vrt, String voiceProvider)
throws CommandPlayerException
{
super(ctx, applicationMode, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION);
this.vrt = vrt;
}
@Override
public void clear() {
super.clear();
if (filesToPlay != null){
filesToPlay.clear();
}
if (mediaPlayer != null){
mediaPlayer.release();
}
mediaPlayer = null;
}
@Override
public void stop(){
if (filesToPlay != null){
filesToPlay.clear();
}
if (mediaPlayer != null){
mediaPlayer.stop();
}
abandonAudioFocus();
}
// Called from the calculating route thread.
@Override
public synchronized void playCommands(CommandBuilder builder) {
if(vrt.isMute()) {
StringBuilder bld = new StringBuilder();
for (String s : builder.execute()) {
bld.append(s).append(' ');
}
if (ctx != null) {
// sendAlertToAndroidWear(ctx, bld.toString());
}
return;
}
filesToPlay.addAll(builder.execute());
// If we have not already started to play audio, start.
if (mediaPlayer == null) {
requestAudioFocus();
// Delay first prompt of each batch to allow BT SCO connection being established
if (ctx != null && ctx.getSettings().AUDIO_STREAM_GUIDANCE.getModeValue(getApplicationMode()) == 0) {
try {
log.debug("Delaying MediaCommandPlayer for BT SCO");
Thread.sleep(ctx.getSettings().BT_SCO_DELAY.get());
} catch (InterruptedException e) {
}
}
}
playQueue();
}
private synchronized void playQueue() {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
performDelays();
File file = getNextFileToPlay();
if (file != null) {
playFile(file);
// Will continue with onCompletion()
} else {
// Release the media player only when we are done speaking.
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
abandonAudioFocus();
}
}
}
/**
* Called when the MediaPlayer is done. The call back is on the main thread.
*/
@Override
public void onCompletion(MediaPlayer mp) {
// Work on the next file to play.
playQueue();
}
private void performDelays() {
int sleep = 0;
while (!filesToPlay.isEmpty() && filesToPlay.get(0).startsWith(DELAY_CONST)) {
String s = filesToPlay.remove(0).substring(DELAY_CONST.length());
try {
sleep += Integer.parseInt(s);
} catch (NumberFormatException e) {
}
}
try {
if (sleep != 0) {
log.debug("Delaying "+sleep);
Thread.sleep(sleep);
}
} catch (InterruptedException e) {
}
}
private File getNextFileToPlay() {
while (!filesToPlay.isEmpty()) {
String f = filesToPlay.remove(0);
if (f != null && voiceDir != null) {
File file = new File(voiceDir, f);
return file;
}
}
return null;
}
/**
* Starts the MediaPlayer playing a file. This method will return immediately.
* OnCompletionListener() will be called when the MediaPlayer is done.
* @param file
*/
private void playFile(File file) {
if (!file.exists()) {
log.error("Unable to play, does not exist: "+file);
playQueue();
return;
}
try {
log.debug("Playing file : " + file); //$NON-NLS-1$
mediaPlayer.reset();
mediaPlayer.setAudioStreamType(streamType);
mediaPlayer.setDataSource(file.getAbsolutePath());
mediaPlayer.prepare();
mediaPlayer.setOnCompletionListener(this);
mediaPlayer.start();
} catch (Exception e) {
log.error("Error while playing voice command", e); //$NON-NLS-1$
playQueue();
}
}
public static boolean isMyData(File voiceDir) {
return new File(voiceDir, CONFIG_FILE).exists();
}
@Override
public boolean supportsStructuredStreetNames() {
return false;
}
}