diff --git a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java index a46bf4248d..23aec32ea1 100644 --- a/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java +++ b/OsmAnd/src/net/osmand/plus/voice/MediaCommandPlayerImpl.java @@ -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 filesToPlay = Collections.synchronizedList(new ArrayList()); 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"); + } + } }