/*
* Copyright 2012 Brendan McCarthy (brendan@oddsoftware.net)
*
* This file is part of Feedscribe.
*
* Feedscribe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3
* as published by the Free Software Foundation.
*
* Feedscribe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Feedscribe. If not, see <http://www.gnu.org/licenses/>.
*/
package net.oddsoftware.android.feedscribe;
import java.io.IOException;
import net.oddsoftware.android.feedscribe.data.Enclosure;
import net.oddsoftware.android.feedscribe.data.FeedItem;
import net.oddsoftware.android.feedscribe.data.FeedManager;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.PowerManager;
public class AudioPlayer
{
private static AudioPlayer mInstance = null;
public static synchronized AudioPlayer getInstance(Context context)
{
if(mInstance == null)
{
mInstance = new AudioPlayer(context.getApplicationContext());
}
return mInstance;
}
private MediaPlayer mMediaPlayer = null;
private boolean mPaused;
private boolean mStarted;
private Context mContext;
private long mItemId;
private long mEnclosureId;
private String mCurrentPath;
NoisyAudioStreamReceiver mNoisyAudioStreamReceiver;
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private AudioPlayer(Context context)
{
mContext = context;
mPaused = false;
mStarted = false;
mItemId = -1;
mEnclosureId = -1;
mNoisyAudioStreamReceiver = new NoisyAudioStreamReceiver();
}
private MediaPlayer getMediaPlayer()
{
if(mMediaPlayer == null)
{
mMediaPlayer = new MediaPlayer();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) { onAudioFinished(); }
});
}
return mMediaPlayer;
}
private void onAudioFinished()
{
playbackStopped();
mPaused = false;
mStarted = false;
if( mItemId > 0 )
{
FeedManager feedManager = FeedManager.getInstance(mContext);
FeedItem item = feedManager.getItemById(mItemId);
if( item != null && (item.mFlags & FeedItem.FLAG_READ) == 0 )
{
item.mFlags = item.mFlags | FeedItem.FLAG_READ;
feedManager.updateItemFlags(item);
}
Enclosure enclosure = feedManager.getEnclosure( mEnclosureId );
if( enclosure != null)
{
enclosure.mPosition = 0;
feedManager.updateEnclosure(enclosure);
}
}
}
public void savePosition()
{
if( mEnclosureId > 0 )
{
FeedManager feedManager = FeedManager.getInstance(mContext);
Enclosure enclosure = feedManager.getEnclosure( mEnclosureId );
MediaPlayer mediaPlayer = getMediaPlayer();
if( enclosure != null )
{
enclosure.mPosition = mediaPlayer.getCurrentPosition();
// update duration
long duration = mediaPlayer.getDuration();
if( duration > 0)
{
enclosure.mDuration = duration;
// check to see if audio is 97% finished and if so, mark it as read
if( (enclosure.mPosition * 100 / enclosure.mDuration) >= 97 )
{
FeedItem item = feedManager.getItemById(mItemId);
if( item != null && (item.mFlags & FeedItem.FLAG_READ) == 0 )
{
item.mFlags = item.mFlags | FeedItem.FLAG_READ;
feedManager.updateItemFlags(item);
}
}
}
if( enclosure.mPosition >= mediaPlayer.getDuration() - 2000)
{
enclosure.mPosition = 0;
}
feedManager.updateEnclosure( enclosure );
}
}
}
public boolean isPaused()
{
Globals.LOG.d("AudioPlayer.isPaused: " + mPaused + " " + getMediaPlayer().isPlaying() + " started " + mStarted );
return mPaused && mStarted;
}
public int getCurrentPosition()
{
return getMediaPlayer().getCurrentPosition();
}
public void seekTo(int position)
{
getMediaPlayer().seekTo(position);
}
public int getDuration()
{
return getMediaPlayer().getDuration();
}
public void seekBy(int i)
{
MediaPlayer mediaPlayer = getMediaPlayer();
int pos = mediaPlayer.getCurrentPosition();
pos += i;
mediaPlayer.seekTo(pos);
}
public void pause()
{
getMediaPlayer().pause();
mPaused = true;
playbackStopped();
}
public void resume()
{
if(mPaused)
{
getMediaPlayer().start();
mPaused = false;
playbackStarted();
}
}
public boolean playPath(String path, int seekPosition)
{
return playThing(path, true, seekPosition);
}
public boolean playUrl(String url, int seekPosition)
{
return playThing(url, false, seekPosition);
}
private boolean playThing(String thing, boolean isLocal, int seekPosition)
{
try
{
MediaPlayer mediaPlayer = getMediaPlayer();
mediaPlayer.reset();
if( isLocal )
{
mediaPlayer.setDataSource(thing);
mCurrentPath = thing;
}
else
{
mediaPlayer.setDataSource(mContext, Uri.parse(thing));
mCurrentPath = null;
}
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.prepare();
if (seekPosition > 0) {
mediaPlayer.seekTo(seekPosition);
}
mediaPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
mediaPlayer.start();
playbackStarted();
mPaused = false;
mStarted = true;
return true;
}
catch(IOException exc)
{
Globals.LOG.e("Error playing track " + thing + " isLocal " + isLocal, exc);
return false;
}
}
public void setItem(long itemId, long enclosureId)
{
mItemId = itemId;
mEnclosureId = enclosureId;
}
public long getItemId()
{
return mItemId;
}
public boolean isPlaying()
{
return getMediaPlayer().isPlaying();
}
public boolean hasStarted()
{
return mStarted;
}
private void playbackStarted()
{
mContext.registerReceiver(mNoisyAudioStreamReceiver, intentFilter);
}
public String getCurrentPath() {
return mCurrentPath;
}
private void playbackStopped()
{
// cancel any current playing notification
savePosition();
NotificationManager notificationManager = (NotificationManager) mContext.getSystemService( Context.NOTIFICATION_SERVICE );
notificationManager.cancel(Globals.NOTIFICATION_PLAYING);
try
{
mContext.unregisterReceiver(mNoisyAudioStreamReceiver);
}
catch(IllegalArgumentException exc)
{
Globals.LOG.w("AudioPlayer.playbackStopped - Error unregistering receiver", exc);
}
}
private class NoisyAudioStreamReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction()))
{
Globals.LOG.d("got headphone plug event");
if(isPlaying())
{
pause();
}
}
}
}
}