/**
*
*/
package gilday.android.powerhour.service;
import android.app.Service;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.v4.content.LocalBroadcastManager;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;
import gilday.android.powerhour.*;
import gilday.android.powerhour.data.PreferenceRepository;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
/**
* @author John Gilday
*
*/
public class PowerHourService extends Service implements ISongPreparedListener, IAudioFocusLostListener {
public static final int NOT_STARTED = 0;
public static final int PLAYING = 1;
public static final int PAUSED = 2;
static final int INTERVAL = 1000;
// public static final int INTERVAL = 200;
public static final String MUSIC_UPDATE_BROADCAST = "com.johnathangilday.powerhour.musicupdatebroadcast";
public static final String PROGRESS_UPDATE_BROADCAST = "com.johnathangilday.powerhour.progressupdatebroadcast";
public static final String PROGRESS_PAUSE_RESUME_BROADCAST = "com.johnathangilday.powerhour.progresspauseresumebroadcast";
public static final String SONGID = "songid";
public static final String PROGRESS = "progress";
public static final String IS_PAUSED = "isPaused";
public static final String ACTION_PLAY_PAUSE = "com.johnathangilday.powerhour.action.playpause";
public static final String ACTION_SKIP = "com.johnathangilday.powerhour.action.skip";
private Timer myTimer;
private int seconds = -1;
private int playingState = NOT_STARTED;
Handler mHandler = new Handler();
private PreferenceRepository powerHourPrefs;
private NowPlayingPlaylistManager playlistManager;
private SongPlayer songPlayer;
private MusicUpdateBroadcastReceiver musicUpdateBroadcastReceiver;
private ProgressUpdateBroadcastReceiver progressUpdateBroadcastReceiver;
@Override
public void onCreate(){
super.onCreate();
powerHourPrefs = new PreferenceRepository(this);
playlistManager = new NowPlayingPlaylistManager(this);
TelephonyManager tm = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
tm.listen(new PhoneStateListener(){
public void onCallStateChanged(int state, String incomingNumber){
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
if(playingState == PLAYING)
playPause();
break;
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
// Register the NotificationSoundClipPlayer so it will play the
// alert clip when the service broadcasts a progress advance
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext());
NotificationSoundClipPlayer drinkNotificationPlayer = new NotificationSoundClipPlayer(getApplicationContext());
NotificationController notificationController;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
notificationController = new JellybeanOngoingNotificationUpdater(getApplicationContext());
} else {
notificationController = new OngoingNotificationUpdater(getApplicationContext());
}
progressUpdateBroadcastReceiver = new ProgressUpdateBroadcastReceiver(drinkNotificationPlayer, notificationController);
musicUpdateBroadcastReceiver = new MusicUpdateBroadcastReceiver(notificationController);
lbm.registerReceiver(progressUpdateBroadcastReceiver, new IntentFilter(PowerHourService.PROGRESS_UPDATE_BROADCAST));
lbm.registerReceiver(progressUpdateBroadcastReceiver, new IntentFilter(PowerHourService.PROGRESS_PAUSE_RESUME_BROADCAST));
lbm.registerReceiver(musicUpdateBroadcastReceiver, new IntentFilter(PowerHourService.MUSIC_UPDATE_BROADCAST));
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
if(playingState == NOT_STARTED && playlistManager.getPlaylistSize() > 0) {
if(playlistManager.getPlaylistSize() <= 0){
throw new ArrayIndexOutOfBoundsException("The song list given to PowerHourService contains no songs!");
}
// Start timer
myTimer = new Timer();
// Initialize new SongPlayer because about to start playing music
songPlayer = new SongPlayer(this, this);
myTimer.schedule(new SecondTimer(), 0, INTERVAL);
}
// PowerHourService handles intents for ACTION_PLAY_PAUSE and ACTION_SKIP
// so external actors (like BroadcastReceivers) may send these actions without binding
if(intent != null) {
String action = intent.getAction();
if(ACTION_PLAY_PAUSE.equals(action)) playPause();
else if(ACTION_SKIP.equals(action)) skip();
}
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
//Log.(TAG, "onBind here");
return (IBinder) mBinder;
}
@Override
public void onDestroy(){
super.onDestroy();
if(playingState != PowerHourService.NOT_STARTED) {
myTimer.cancel();
playingState = PowerHourService.NOT_STARTED;
songPlayer.dispose();
}
// Clear progressUpdateReceiver
progressUpdateBroadcastReceiver.unregisterUpdateListener();
musicUpdateBroadcastReceiver.unRegisterUpdateListener();
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getApplicationContext());
lbm.unregisterReceiver(progressUpdateBroadcastReceiver);
lbm.unregisterReceiver(musicUpdateBroadcastReceiver);
// Clear playlist
playlistManager.clearPlaylist();
}
/**
* Loads and plays the next song in the list. Takes care of shuffling here.
* @return the id (in terms of the Android MusicProvider) of the song that was just loaded
*/
int loadNextSong() {
// Do expensive work before we reset the media player so we don't "skip a beat" bahahaha
// Advance to next song
int songId = playlistManager.advancePlaylist();
if(songId == -1) {
return -1;
}
try{
songPlayer.prepareNextSong(songId, this);
} catch (IllegalStateException e) {
toastError("Nasty error has occured with the Android VM. Please wait for the next song");
e.printStackTrace();
return -1;
} catch (IOException e) {
toastError("Song corrupted! Error retrieving song with id: " + songId);
e.printStackTrace();
return -1;
}
// Send update
Intent intent = new Intent();
intent.putExtra(PowerHourService.SONGID, songId);
intent.setAction(PowerHourService.MUSIC_UPDATE_BROADCAST);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
return songId;
}
@Override
public void onSongPrepared(SongPlayer audioPlayerManager) {
// If the power hour is playing..
if(playingState == PLAYING) {
// .. press play on the audio manager
audioPlayerManager.play();
}
}
@Override
public void onAudioFocusLost() {
// The app has lost focus indefinitely to another music player
// Pause the Power Hour until user manually plays
if(playingState == PowerHourService.PLAYING)
playPause();
}
private void toastError(String message) {
// pass parameter to a final String so inner class can access it
final String _message = message;
mHandler.post(new Runnable() {
public void run() {
Toast.makeText(PowerHourService.this, _message, Toast.LENGTH_LONG).show();
}
});
}
void playPause(){
if(playingState == PowerHourService.PLAYING) {
songPlayer.pause();
myTimer.cancel();
playingState = PowerHourService.PAUSED;
} else {
songPlayer.play();
myTimer = new Timer();
myTimer.schedule(new SecondTimer(), 1000, INTERVAL);
playingState = PowerHourService.PLAYING;
}
// Send Progress Pause / Resume Broadcast
Intent broadcast = new Intent();
broadcast.putExtra(PowerHourService.IS_PAUSED, (playingState == PowerHourService.PAUSED));
broadcast.setAction(PROGRESS_PAUSE_RESUME_BROADCAST);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(broadcast);
}
int skip() {
if(!playlistManager.isPlayingLastSong()){
return loadNextSong();
} else {
return -1;
}
}
private final IPowerHourService mBinder = new PowerHourServiceInterface();
private class PowerHourServiceInterface extends Binder implements IPowerHourService
{
public void playPause() {
PowerHourService.this.playPause();
}
public int skip() {
return PowerHourService.this.skip();
}
public int getPlayingState() {
return playingState;
}
public int getProgress(){
return seconds;
}
public int getCurrentSong() {
return playlistManager.getCurrentSong();
}
}
private class SecondTimer extends TimerTask{
@Override
public void run() {
//Log.(TAG, "tick!");
seconds++;
playingState = PowerHourService.PLAYING;
if(seconds % 60 == 0) {
int minutes = seconds / 60;
// If the power hour is over, or if the list is too small and there are no more songs to load,
// end the power hour
int duration = powerHourPrefs.getDuration();
if(minutes >= duration || playlistManager.isPlayingLastSong()){
// Tear down
stopSelf();
return;
}
loadNextSong();
// Send progress update
Intent intent = new Intent();
intent.putExtra(PowerHourService.PROGRESS, minutes);
intent.setAction(PowerHourService.PROGRESS_UPDATE_BROADCAST);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
}
}
}