com.somexapps.wyre.services.MediaService.java Source code

Java tutorial

Introduction

Here is the source code for com.somexapps.wyre.services.MediaService.java

Source

package com.somexapps.wyre.services;

import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.media.RatingCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v7.app.NotificationCompat;
import android.util.Log;

import com.somexapps.wyre.R;
import com.somexapps.wyre.models.Song;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;

import java.io.IOException;
import java.util.ArrayList;

/**
 * Copyright 2014, 2015 Michael Limb, paulruiz
 * Code adapted from:
 * https://github.com/PaulTR/AndroidDemoProjects/blob/master/MediaSessionwithMediaStyleNotification/app/src/main/java/com/ptrprograms/mediasessionwithmediastylenotification/MediaPlayerService.java
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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.
 */
public class MediaService extends Service implements MediaPlayer.OnPreparedListener {
    private final String TAG = MediaService.class.getSimpleName();

    public final static String ACTION_PLAY = "action_play";
    public final static String ACTION_PAUSE = "action_pause";
    public final static String ACTION_STOP = "action_stop";
    public final static String ACTION_NEXT = "action_next";
    public final static String ACTION_PREVIOUS = "action_previous";

    public final static String EXTRA_SONG_TO_PLAY = "song_to_play";
    public final static String EXTRA_SONG_LIST = "song_list";

    private final static String MEDIA_SESSION_TAG = "media_session_tag";
    private final static int MEDIA_NOTIFICATION_REQUEST_CODE = 1;

    /**
     * The list of songs in the queue.
     */
    private ArrayList<Song> songsList;

    /**
     * The data for the currently playing song.
     */
    private Song playingSong;

    /**
     * The index of the playing song in the list of songs
     */
    private int playingSongIndex;

    /**
     * Boolean to tell whether or not the MediaPlayer is paused.
     */
    private boolean isPaused = false;

    /**
     * The component name used by the media session for identifying the controlling class
     */
    private final static ComponentName mediaSessionComponentName = new ComponentName("com.somexapps.ripple",
            "com.somexapps.ripple.services.MediaService");

    private MediaControllerCompat mediaController;
    //private MediaSessionManager mediaSessionManager;

    /**
     * The session for the service
     */
    private MediaSessionCompat mediaSession;

    /**
     * The media player for the service.
     */
    private MediaPlayer mediaPlayer;

    /**
     * Audio manager, used for querying device state and such.
     */
    //private AudioManager audioManager;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Make sure our controls have been initialized
        if (mediaSession == null) {
            initMediaSessions();
        }

        // Pass the intent to handling function
        handleIntent(intent);

        return super.onStartCommand(intent, flags, startId);
    }

    private void initMediaSessions() {
        // Initialize player
        mediaPlayer = new MediaPlayer();

        // Add listener to handle completion of song
        mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
            @Override
            public void onCompletion(MediaPlayer mp) {
                // Send intent to play next song in MediaService list
                Intent nextSong = new Intent(getApplicationContext(), MediaService.class);
                nextSong.setAction(ACTION_NEXT);
                startService(nextSong);
            }
        });

        // Set up session and controller
        // Create the media session
        mediaSession = new MediaSessionCompat(this, MEDIA_SESSION_TAG, mediaSessionComponentName, null);

        // Set up controller
        try {
            mediaController = new MediaControllerCompat(getApplicationContext(), mediaSession.getSessionToken());
        } catch (RemoteException e) {
            Log.e(TAG, "Unable to set up media controller: " + e.getMessage());
        }

        // Set up callbacks for media session
        mediaSession.setCallback(new MediaSessionCompat.Callback() {
            @Override
            public void onPlay() {
                super.onPlay();
                Log.d(TAG, "Playing media");
                buildNotification(generateAction(R.drawable.ic_pause_white_36dp,
                        getString(R.string.media_service_notification_pause), ACTION_PAUSE));
            }

            @Override
            public void onPause() {
                super.onPause();
                Log.d(TAG, "Pausing media");
                buildNotification(generateAction(R.drawable.ic_play_arrow_white_36dp,
                        getString(R.string.media_service_notification_play), ACTION_PLAY));
            }

            @Override
            public void onSkipToNext() {
                super.onSkipToNext();
                Log.d(TAG, "Skipping to next song");

                // Update the notification with the next song info
                buildNotification(generateAction(R.drawable.ic_pause_white_36dp,
                        getString(R.string.media_service_notification_pause), ACTION_PAUSE));
            }

            @Override
            public void onSkipToPrevious() {
                super.onSkipToPrevious();
                Log.d(TAG, "Skipping to previous song");

                // Update the notification with the previous song info
                buildNotification(generateAction(R.drawable.ic_pause_white_36dp,
                        getString(R.string.media_service_notification_pause), ACTION_PAUSE));
            }

            @Override
            public void onFastForward() {
                super.onFastForward();
            }

            @Override
            public void onRewind() {
                super.onRewind();
            }

            @Override
            public void onStop() {
                super.onStop();
            }

            @Override
            public void onSeekTo(long pos) {
                super.onSeekTo(pos);
            }

            @Override
            public void onSetRating(RatingCompat rating) {
                super.onSetRating(rating);
            }
        });
    }

    private void handleIntent(Intent intent) {
        if (intent != null) {
            int songToPlay = -1;
            if (intent.getExtras() != null) {
                // Get list of songs
                songsList = intent.getParcelableArrayListExtra(EXTRA_SONG_LIST);

                // See if song to play was passed
                songToPlay = intent.getExtras().getInt(EXTRA_SONG_TO_PLAY, -1);
            }

            switch (intent.getAction()) {
            case ACTION_PLAY:
                // Play the song
                playSong(songToPlay);

                // Notify transport controls
                mediaController.getTransportControls().play();

                break;
            case ACTION_STOP:
                // Notify transport controls
                mediaController.getTransportControls().stop();

                break;
            case ACTION_PAUSE:
                if (mediaPlayer != null && mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    isPaused = true;
                }

                // Notify transport controls
                mediaController.getTransportControls().pause();

                break;
            case ACTION_NEXT:
                // Switch to next song if one exists
                if (playingSongIndex + 1 < songsList.size()) {
                    // Go to next song in list
                    playingSongIndex++;

                    // Play the song
                    playSong(playingSongIndex);
                }

                // Notify transport controls
                mediaController.getTransportControls().skipToNext();

                break;
            case ACTION_PREVIOUS:
                // Switch to previous song if one exists
                if (playingSongIndex - 1 >= 0) {
                    // Go to previous song in list
                    playingSongIndex--;

                    // Play the song
                    playSong(playingSongIndex);
                }

                // Notify transport controls
                mediaController.getTransportControls().skipToPrevious();

                break;
            }
        }
    }

    private void playSong(int indexOfSongToPlay) {
        Song toPlay = null;
        if (indexOfSongToPlay != -1) {
            // Save the index
            playingSongIndex = indexOfSongToPlay;

            toPlay = songsList.get(indexOfSongToPlay);
        }

        if (toPlay != null) {
            // Make sure song is not paused and song we're trying to play is a different one
            if (!isPaused || !toPlay.getData().equals(playingSong.getData())) {
                // Get URI for media
                // Save playing song uri for later reference
                playingSong = toPlay;

                // Reset the media player if not null, otherwise recreate it
                if (mediaPlayer != null) {
                    mediaPlayer.reset();
                } else {
                    mediaPlayer = new MediaPlayer();
                }

                // Try to prepare the media for playing
                try {
                    // Set the data source from the grabbed URI
                    mediaPlayer.setDataSource(playingSong.getData());

                    // Prepare asynchronously, listener will get called after preparation
                    mediaPlayer.setOnPreparedListener(this);
                    mediaPlayer.prepareAsync();
                } catch (IOException e) {
                    // TODO: Handle this error
                    // Print it to logcat
                    Log.e(TAG, e.getMessage());
                }
            } else {
                mediaPlayer.start();
                isPaused = false;
            }
        } else if (isPaused) {
            mediaPlayer.start();
            isPaused = false;
        }
    }

    private void buildNotification(android.support.v4.app.NotificationCompat.Action action) {
        // Initialize style
        NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle();

        // Build intent/pending intent
        Intent intent = new Intent(getApplicationContext(), MediaService.class);
        intent.setAction(ACTION_STOP);
        // TODO: Should this have 0 as the flag?
        PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(),
                MEDIA_NOTIFICATION_REQUEST_CODE, intent, 0);

        // Get notification manager
        final NotificationManagerCompat managerCompat = NotificationManagerCompat.from(getApplicationContext());

        // Create media notification
        final android.support.v4.app.NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_album_white_48dp))
                .setContentTitle(playingSong.getTitle()).setContentText(playingSong.getArtist())
                .setDeleteIntent(pendingIntent).setStyle(style);

        // Check to see if we have a next or previous song to play
        boolean previousDisabled = false;
        boolean nextDisabled = false;

        if (songsList.size() == 1) {
            previousDisabled = true;
            nextDisabled = true;
        } else if (playingSongIndex == 0) {
            previousDisabled = true;
        } else if (playingSongIndex == songsList.size() - 1) {
            nextDisabled = true;
        }

        // Add previous action if there is previous media
        if (!previousDisabled) {
            builder.addAction(generateAction(R.drawable.ic_skip_previous_white_36dp,
                    getString(R.string.media_service_notification_prev), ACTION_PREVIOUS));
        }

        // Add play/pause action
        builder.addAction(action);

        // Add next action if there is next media
        if (!nextDisabled) {
            builder.addAction(generateAction(R.drawable.ic_skip_next_white_36dp,
                    getString(R.string.media_service_notification_next), ACTION_NEXT));
        }

        // Show buttons based on state
        if (previousDisabled && nextDisabled) {
            style.setShowActionsInCompactView(0);
        } else if (previousDisabled || nextDisabled) {
            style.setShowActionsInCompactView(0, 1);
        } else {
            style.setShowActionsInCompactView(0, 1, 2);
        }

        /**
         * Check if we are going to a play state by checking to see if the middle
         * action is getting set to Pause
         */
        if (action.getTitle().equals(getString(R.string.media_service_notification_pause))) {
            builder.setOngoing(true);
        } else {
            builder.setOngoing(false);
        }

        // Check if we have a image path
        if (playingSong.getAlbumArtPath() != null && playingSong.getAlbumArtPath().startsWith("http")) {
            // Set up image target
            Target iconTarget = new Target() {
                @Override
                public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
                    // Set icon
                    builder.setLargeIcon(bitmap);

                    // Update notification
                    managerCompat.notify(MEDIA_NOTIFICATION_REQUEST_CODE, builder.build());
                }

                @Override
                public void onBitmapFailed(Drawable errorDrawable) {
                    // do nothing
                }

                @Override
                public void onPrepareLoad(Drawable placeHolderDrawable) {
                    // do nothing
                }
            };

            // Start the image load
            Picasso.with(getApplicationContext()).load(Uri.parse(playingSong.getAlbumArtPath())).into(iconTarget);
        }

        // Update the notification
        managerCompat.notify(MEDIA_NOTIFICATION_REQUEST_CODE, builder.build());
    }

    private android.support.v4.app.NotificationCompat.Action generateAction(int icon, String title,
            String intentAction) {
        Intent intent = new Intent(getApplicationContext(), MediaService.class);
        intent.setAction(intentAction);
        PendingIntent pendingIntent = PendingIntent.getService(getApplicationContext(),
                MEDIA_NOTIFICATION_REQUEST_CODE, intent, 0);

        return new android.support.v4.app.NotificationCompat.Action.Builder(icon, title, pendingIntent).build();
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        // Play the song
        mediaPlayer.start();

        // Let transport controls know we're playing to build the notification
        mediaController.getTransportControls().play();
    }

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

    @Override
    public boolean onUnbind(Intent intent) {
        // Release media session
        if (mediaSession != null) {
            mediaSession.release();
        }

        // Release media player
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }

        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        // Release the music player if it's created
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
}