/*******************************************************************************
* Copyright 2012 Crazywater
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package de.knufficast.player;
import java.io.FileInputStream;
import java.io.IOException;
import android.app.Service;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import de.knufficast.App;
import de.knufficast.events.PlayerErrorEvent;
import de.knufficast.logic.db.DBEpisode;
import de.knufficast.logic.db.DBEpisode.PlayState;
import de.knufficast.util.Callback;
import de.knufficast.util.file.ExternalFileUtil;
/**
* An audio player for episodes, based on the android {@link MediaPlayer}.
*
* @author crazywater
*
*/
public class PlayerService extends Service {
private MediaPlayer mediaPlayer;
private final IBinder binder = new PlayerBinder();
private DBEpisode episode;
private boolean prepared;
private final OnErrorListener onErrorListener = new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
prepared = false;
App.get().getEventBus().fireEvent(new PlayerErrorEvent());
return true;
}
};
/**
* Prepares the player to play the episode.
*/
public void setEpisode(DBEpisode episode) {
if (episode == this.episode) {
return;
}
prepared = false;
this.episode = episode;
mediaPlayer.reset();
if (episode != null) {
try {
FileInputStream inputStream;
inputStream = new ExternalFileUtil(getApplicationContext())
.read(episode.getFileLocation());
mediaPlayer.setDataSource(inputStream.getFD());
mediaPlayer.prepareAsync();
} catch (IOException e) {
App.get().getEventBus().fireEvent(new PlayerErrorEvent());
}
}
}
/**
* Whether this player is prepared to play an episode.
*/
public boolean hasEpisode() {
return episode != null;
}
public void play() {
if (prepared) {
mediaPlayer.start();
episode.setPlayState(PlayState.STARTED_PLAYING);
}
}
public void pause() {
if (prepared) {
int currentPosition = mediaPlayer.getCurrentPosition();
if (currentPosition > episode.getSeekLocation()) {
episode.setSeekLocation(currentPosition);
}
mediaPlayer.pause();
}
}
public boolean isPlaying() {
return mediaPlayer.isPlaying();
}
public boolean isPrepared() {
return prepared;
}
public void seekTo(int msec) {
if (prepared) {
mediaPlayer.seekTo(msec);
episode.setSeekLocation(msec);
}
}
public int getDuration() {
return mediaPlayer.getDuration();
}
/**
* Returns the current seek position in milliseconds.
*/
public int getCurrentPosition() {
return mediaPlayer.getCurrentPosition();
}
/**
* Sets a callback to be called upon completion of playing the episode.
*/
void setOnCompletionCallback(final Callback<Void> callback) {
mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
// set the play state to "finished" as well before we call the callback
if (episode != null) {
episode.setPlayState(PlayState.FINISHED);
episode.setSeekLocation(mediaPlayer.getCurrentPosition());
}
prepared = false;
callback.call(null);
}
});
}
/**
* Sets a callback to be called upon preparing for playing the episode.
*/
void setOnPreparedCallback(final Callback<Void> callback) {
mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
int seekLocation = episode.getSeekLocation();
if (episode.getSeekLocation() > 0) {
mediaPlayer.seekTo(seekLocation);
}
prepared = true;
callback.call(null);
}
});
}
class PlayerBinder extends Binder {
PlayerService getService() {
return PlayerService.this;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
// do NOT call the completion callback on errors
mediaPlayer.setOnErrorListener(onErrorListener);
}
@Override
public void onDestroy() {
mediaPlayer.release();
super.onDestroy();
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}