Flow control for playing voice commands changed to clarify flow
If API LEVEL > 8: AudioManager is used to gain temporary audio focus.
This commit is contained in:
parent
8a532752eb
commit
a79e7e5da7
1 changed files with 112 additions and 65 deletions
|
@ -11,7 +11,9 @@ import net.osmand.plus.OsmandSettings;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
|
||||
/**
|
||||
|
@ -19,7 +21,7 @@ import android.media.MediaPlayer;
|
|||
* It gets commands from input, analyze what files should be played and play
|
||||
* them using media player
|
||||
*/
|
||||
public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer {
|
||||
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
|
||||
|
@ -29,15 +31,17 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer {
|
|||
// playing media
|
||||
private MediaPlayer mediaPlayer;
|
||||
// indicates that player is ready to play first file
|
||||
private volatile boolean playNext = true;
|
||||
private List<String> filesToPlay = Collections.synchronizedList(new ArrayList<String>());
|
||||
private int streamType;
|
||||
private final Context mCtx;
|
||||
private AudioFocusHelper mAudioFocusHelper;
|
||||
|
||||
|
||||
public MediaCommandPlayerImpl(Context ctx, OsmandSettings settings, String voiceProvider)
|
||||
throws CommandPlayerException
|
||||
{
|
||||
super(ctx, settings, voiceProvider, CONFIG_FILE, MEDIA_VOICE_VERSION);
|
||||
mCtx = ctx;
|
||||
this.streamType = settings.AUDIO_STREAM_GUIDANCE.get();
|
||||
}
|
||||
|
||||
|
@ -53,82 +57,125 @@ public class MediaCommandPlayerImpl extends AbstractPrologCommandPlayer {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void playCommands(CommandBuilder builder){
|
||||
public synchronized void playCommands(CommandBuilder builder) {
|
||||
filesToPlay.addAll(builder.execute());
|
||||
playQueue();
|
||||
|
||||
// If we have not already started to play audio, start.
|
||||
if (mediaPlayer == null) {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 8) {
|
||||
mAudioFocusHelper = new AudioFocusHelper(mCtx);
|
||||
} else {
|
||||
mAudioFocusHelper = null;
|
||||
}
|
||||
if (mAudioFocusHelper != null)
|
||||
mAudioFocusHelper.requestFocus();
|
||||
playQueue();
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void playQueue() {
|
||||
if (!playNext)
|
||||
return;
|
||||
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;
|
||||
|
||||
if (mAudioFocusHelper != null)
|
||||
mAudioFocusHelper.abandonFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
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)
|
||||
Thread.sleep(sleep);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private File getNextFileToPlay() {
|
||||
while (!filesToPlay.isEmpty()) {
|
||||
String f = filesToPlay.remove(0);
|
||||
if (f != null && voiceDir != null) {
|
||||
// if(voiceZipFile != null){
|
||||
// ZipEntry entry = voiceZipFile.getEntry(f);
|
||||
// exists = entry != null;
|
||||
// voiceZipFile.getInputStream(entry);
|
||||
//
|
||||
// } else {
|
||||
File file = new File(voiceDir, f);
|
||||
if (file.exists()) {
|
||||
log.debug("Playing file : " + f); //$NON-NLS-1$
|
||||
playNext = false;
|
||||
try {
|
||||
if (mediaPlayer == null) {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
}
|
||||
|
||||
// Can't play sound file from zip it seams to be impossible only unpack and play!!!
|
||||
mediaPlayer.setAudioStreamType(streamType);
|
||||
mediaPlayer.setDataSource(file.getAbsolutePath());
|
||||
mediaPlayer.prepare();
|
||||
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
|
||||
@Override
|
||||
public void onCompletion(MediaPlayer mp) {
|
||||
// Reset prepares to speak again. Don't release because if we have more to
|
||||
// say, we don't want our speech interrupted by other audio, such as music
|
||||
// or a podcast. We will release when we are done speaking.
|
||||
mp.reset();
|
||||
int sleep = 60;
|
||||
boolean delay = true;
|
||||
while (!filesToPlay.isEmpty() && delay) {
|
||||
delay = filesToPlay.get(0).startsWith(DELAY_CONST);
|
||||
if (delay) {
|
||||
String s = filesToPlay.remove(0).substring(DELAY_CONST.length());
|
||||
try {
|
||||
sleep += Integer.parseInt(s);
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Thread.sleep(sleep);
|
||||
} catch (InterruptedException e) {
|
||||
}
|
||||
playNext = true;
|
||||
playQueue();
|
||||
}
|
||||
});
|
||||
|
||||
mediaPlayer.start();
|
||||
} catch (Exception e) {
|
||||
log.error("Error while playing voice command", e); //$NON-NLS-1$
|
||||
playNext = true;
|
||||
}
|
||||
} else {
|
||||
log.info("Play file not found : " + f); //$NON-NLS-1$
|
||||
}
|
||||
if (file.exists())
|
||||
return file;
|
||||
else
|
||||
log.error("Unable to play, does not exist: "+file);
|
||||
}
|
||||
}
|
||||
// Release the media player only when we are done speaking.
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private void playFile(File file) {
|
||||
log.debug("Playing file : " + file); //$NON-NLS-1$
|
||||
try {
|
||||
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();
|
||||
}
|
||||
|
||||
// We Use API level 8 calls here, suppress warnings.
|
||||
@SuppressLint("NewApi")
|
||||
public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
|
||||
private Context mContext;
|
||||
private AudioManager mAudioManager;
|
||||
|
||||
public AudioFocusHelper(Context context) {
|
||||
mContext = context;
|
||||
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
|
||||
}
|
||||
|
||||
public boolean requestFocus() {
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
|
||||
mAudioManager.requestAudioFocus(this, streamType, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
|
||||
}
|
||||
|
||||
public boolean abandonFocus() {
|
||||
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
|
||||
mAudioManager.abandonAudioFocus(this);
|
||||
}
|
||||
@Override
|
||||
public void onAudioFocusChange(int focusChange) {
|
||||
if (focusChange == AudioManager.AUDIOFOCUS_GAIN)
|
||||
playQueue();
|
||||
else
|
||||
log.error("Unable to gain audio focus");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue