com.ecoplayer.beta.MusicService.java Source code

Java tutorial

Introduction

Here is the source code for com.ecoplayer.beta.MusicService.java

Source

/*
 * Author:   Ivan Carballo Fernandez (icf1e11@soton.ac.uk) 
 * Project:   EcoPlayer - Battery-friendly music player for Android (MSc project at University of Southampton)
 * Date:   13-07-2012
 * License: Copyright (C) 2012 Ivan Carballo. 
 */
package com.ecoplayer.beta;

import java.io.IOException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.ContentUris;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.IBinder;
import android.os.PowerManager;

//Music service for playing music from a play queue. 
//It support several commands such as start, play, next, previous, etc... 
public class MusicService extends Service implements MediaPlayer.OnPreparedListener {
    public static final String MUSIC_UPDATE = "com.ecoplayer.beta.MUSIC_UPDATE";
    public static final String SONG_CHANGED = "com.ecoplayer.beta.SONG_CHANGED";
    public static final String PLAYER_STATE_CHANGED = "com.ecoplayer.beta.PLAYER_STATE_CHANGED";
    public static final String ACTION_PLAY = "com.ecoplayer.beta.PLAY";
    public static final String ACTION_PAUSE = "com.ecoplayer.beta.PAUSE";
    public static final String ACTION_NEXT = "com.ecoplayer.beta.NEXT";
    public static final String ACTION_PREVIOUS = "com.ecoplayer.beta.PREVIOUS";
    // If the previous song button is pressed before the song has been played
    // for longer than MIN_TIME_GOING_PREVIOUS
    // the player will move to the previous song instead of starting again the
    // current one.
    public static final int MIN_TIME_GOING_PREVIOUS = 1500; // mill, so 1.5s
    private static final String LOG_TAG = "MusicService";
    public static int NOTIFICATION_ID = 1337;
    private MediaPlayer mMediaPlayer = null;
    private NotificationCompat.Builder notiBuilder;
    private PlayQueue playQueue = null;
    // True if the MediaPlayer has been initialised but not prepared
    private boolean isMediaPlayerInit = false;
    // True if the MediaPlayer is paused.
    private boolean isMediaPlayerPaused = false;
    private boolean isPausedBecauseOfButton = false;
    // Reference to the son that is been played
    private Song songPlaying = null;
    private NotificationManager mNotificationManager;
    // Cpu service profiler name
    private ComponentName cpuProfilerName = null;
    private Intent intentCpuProfilerService;

    @Override
    public void onCreate() {
        super.onCreate();
        playQueue = PlayQueue.getInstance();
        notiBuilder = new NotificationCompat.Builder(getApplicationContext()).setSmallIcon(R.drawable.ic_launcher)
                .setContentTitle(getResources().getString(R.string.app_name)).setContentIntent(buildPendingIntent())
                .setContentText(getResources().getString(R.string.tap_to_play));
        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        isMediaPlayerInit = initMediaPlayer();
        startCpuProfilerService();
    }

    // This method initialize the Media Player
    private boolean initMediaPlayer() {
        startForeground(NOTIFICATION_ID, notiBuilder.getNotification());
        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        int result = audioManager.requestAudioFocus(audioFocusChangeList, AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN);
        if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
            mMediaPlayer = new MediaPlayer(); // initialize it here
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnCompletionListener(onCompletion);
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
            // prevent CPU from going to sleep
            mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
            return true;
        } else {
            Log.w(LOG_TAG, "The service hasn't got the audio focus");
        }
        return false;
    }

    // Send message to Activity, if the first parameter is true it means the
    // player has change from palying to
    // paused or vice versa. If the second is true it means that a new song is
    // starting to be payed.
    private void sendMusicUpdateToActivity(boolean playerStateChanged, boolean song_changed) {
        if (song_changed) {
            Log.d("SongBlue", "Notified song chaged, current song " + songPlaying.getTitle());
        }
        Intent intentMusicUpdate = new Intent(MUSIC_UPDATE);
        intentMusicUpdate.putExtra(PLAYER_STATE_CHANGED, playerStateChanged);
        intentMusicUpdate.putExtra(SONG_CHANGED, song_changed);
        sendBroadcast(intentMusicUpdate);
    }

    // Play a given song. This method is async.
    private boolean playSong(Song song) {
        resetSongPlayingRef();
        if (song != null) {
            if (isMediaPlayerInit) {
                mMediaPlayer.reset();
                try {
                    mMediaPlayer.setDataSource(getApplicationContext(), getUriFromSong(song));
                    // prepare async to not block main thread
                    mMediaPlayer.prepareAsync();
                    songPlaying = song;
                    // Set the flag in the song to playing
                    songPlaying.setPlaying(true);
                    return true;
                } catch (IllegalArgumentException e) {
                    Log.e(e.getClass().getName(), e.getMessage(), e);
                } catch (SecurityException e) {
                    Log.e(e.getClass().getName(), e.getMessage(), e);
                } catch (IllegalStateException e) {
                    Log.e(e.getClass().getName(), e.getMessage(), e);
                } catch (IOException e) {
                    Log.e(e.getClass().getName(), e.getMessage(), e);
                }
            }
        } else {
            Log.e(getClass().getName(), "Error retrieving next song from queue, song is null");
        }
        return false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handleCommand(intent);
        // We want this service to continue running until it is explicitly
        // stopped, so return sticky.
        return START_STICKY;
    }

    // Handle the commands received from the Activities.
    private void handleCommand(Intent intent) {
        // Play next song in the queue, also use to start with the first song.
        if (intent.getAction().equals(ACTION_NEXT)) {
            playSong(playQueue.getNext());
            // Pause the song
        } else if (intent.getAction().equals(ACTION_PAUSE)) {
            if (isMediaPlayerInit) {
                if (mMediaPlayer.isPlaying()) {
                    mMediaPlayer.pause();
                    isPausedBecauseOfButton = true;
                    onPause();
                }
            }
            // Start the song after pausing
        } else if (intent.getAction().equals(ACTION_PLAY)) {
            if (isMediaPlayerInit) {
                if (!playQueue.isEmpty()) {
                    if (isMediaPlayerPaused) {
                        mMediaPlayer.start();
                        sendMusicUpdateToActivity(true, false);
                        onPlaying();
                    } else {
                        playSong(playQueue.getCurrent());
                    }
                }
            }
            // Play the previous song in the play queue.
        } else if (intent.getAction().equals(ACTION_PREVIOUS)) {
            if (mMediaPlayer.getCurrentPosition() <= MIN_TIME_GOING_PREVIOUS) {
                playSong(playQueue.getPrevious());
            } else {
                playSong(playQueue.getCurrent());
            }
        }
    }

    // Called after starting to play or resuming
    private void onPlaying() {
        isMediaPlayerPaused = false;
        isPausedBecauseOfButton = false;
        playQueue.setPlaying(true);
        mNotificationManager.notify(NOTIFICATION_ID,
                notiBuilder.setContentIntent(buildPendingIntent()).setContentText(
                        getResources().getString(R.string.playing) + " " + playQueue.getCurrent().getTitle())
                        .getNotification());
    }

    // Called after pausing the music player
    private void onPause() {
        isMediaPlayerPaused = true;
        playQueue.setPlaying(false);
        sendMusicUpdateToActivity(true, false);
        mNotificationManager.notify(NOTIFICATION_ID, notiBuilder.setContentIntent(buildPendingIntent())
                .setContentText(getResources().getString(R.string.paused)).getNotification());
    }

    // Returns the URI of a given Song object
    public Uri getUriFromSong(Song song) {
        return ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                song.getId());
    }

    @Override
    public void onDestroy() {
        stopForeground(true);
        if (mMediaPlayer.isPlaying())
            mMediaPlayer.stop();
        mMediaPlayer.release();
        mMediaPlayer = null;
        stopCpuProfilerService();
        super.onDestroy();
    }

    /** Called when MediaPlayer is ready */
    public void onPrepared(MediaPlayer player) {
        player.start();
        sendMusicUpdateToActivity(true, true);
        onPlaying();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    // Reset the songPlaying var to null
    private void resetSongPlayingRef() {
        if (songPlaying != null) {
            // Set flag false and set reference to the current song to null
            Log.d("SongBlue", "Reseting song " + songPlaying.getTitle());
            songPlaying.setPlaying(false);
            songPlaying = null;
        }

    }

    OnCompletionListener onCompletion = new OnCompletionListener() {

        @Override
        public void onCompletion(MediaPlayer mp) {
            resetSongPlayingRef();
            // Play next
            playSong(playQueue.getNext());
        }
    };

    OnAudioFocusChangeListener audioFocusChangeList = new OnAudioFocusChangeListener() {

        @Override
        public void onAudioFocusChange(int focusChange) {
            switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // If is not paused because the pause button was pressed resume.
                if (!isPausedBecauseOfButton) {
                    // resume playback
                    if (mMediaPlayer == null)
                        // When focus is gained for first time or is lost for a
                        // long time
                        isMediaPlayerInit = initMediaPlayer();
                    else if (!mMediaPlayer.isPlaying()) {
                        // After losing focus for a short time
                        mMediaPlayer.start();
                        sendMusicUpdateToActivity(true, false);
                        onPlaying();
                    } else {
                        // Back from AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK
                        mMediaPlayer.setVolume(1.0f, 1.0f);
                    }
                }
                break;

            case AudioManager.AUDIOFOCUS_LOSS:
                // Lost focus for an unbounded amount of time: stop playback and
                // release media player
                if (mMediaPlayer != null) {
                    if (mMediaPlayer.isPlaying()) {
                        mMediaPlayer.stop();
                        sendMusicUpdateToActivity(true, false);
                        onPause();
                    }
                    MusicService.this.stopForeground(true);
                    mMediaPlayer.release();
                    isMediaPlayerInit = false;
                    mMediaPlayer = null;
                }
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // Lost focus for a short time, but we have to stop
                // playback. We don't release the media player because playback
                // is likely to resume
                if (mMediaPlayer != null) {
                    if (mMediaPlayer.isPlaying()) {
                        mMediaPlayer.pause();
                        sendMusicUpdateToActivity(true, false);
                        onPause();
                    }
                }
                break;

            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // Lost focus for a short time, but it's ok to keep playing
                // at an attenuated level
                if (mMediaPlayer != null) {
                    if (mMediaPlayer.isPlaying())
                        mMediaPlayer.setVolume(0.1f, 0.1f);
                }
                break;
            }

        }
    };

    private PendingIntent buildPendingIntent() {
        Intent intent = new Intent(getApplicationContext(), MainActivity.class);
        if (isMediaPlayerInit) {
            if (mMediaPlayer.isPlaying())
                intent.putExtra(MainActivity.EXTRA_FRAGMENT_ID, MainActivity.FRAGMENT_PLAY_QUEUE);
        }
        return PendingIntent.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }

    // Starts service com.byivan.cpufrequencies.CpuProfilerService that saves
    // logs about the time in each CPU frequency. The logs are located in
    // <external_storage>/cpu_frequencies/time_in_state_logs_<date>.csv
    // It's only started if R.bool.cpu_profiling is true.
    private void startCpuProfilerService() {
        if (getResources().getBoolean(R.bool.cpu_profiling)) {
            // If serviceCpuName is null it means the service hasn't been
            // started yet.
            if (cpuProfilerName == null) {
                String action = "com.byivan.cpufrequencies.action.PROFILING_TIME_IN_STATE";
                intentCpuProfilerService = new Intent(action);
                intentCpuProfilerService.addCategory("com.byivan.cpufrequencies.category.DEFAULT");
                cpuProfilerName = startService(intentCpuProfilerService);
                if (cpuProfilerName == null)
                    Log.e(getClass().getName(), "Service for action=" + action + " cannot be started");
                else
                    Log.i(getClass().getName(), "Service " + cpuProfilerName.getClassName() + " has been started");
            }
        }
    }

    // Stops service com.byivan.cpufrequencies.CpuProfilerService if enabled
    private void stopCpuProfilerService() {
        if (cpuProfilerName != null && intentCpuProfilerService != null)
            stopService(intentCpuProfilerService);
    }
}