org.y20k.transistor.PlayerService.java Source code

Java tutorial

Introduction

Here is the source code for org.y20k.transistor.PlayerService.java

Source

/**
 * PlayerService.java
 * Implements the app's playback background service
 * The player service does xyz
 *
 * This file is part of
 * TRANSISTOR - Radio App for Android
 *
 * Copyright (c) 2015 - Y20K.org
 * Licensed under the MIT-License
 * http://opensource.org/licenses/MIT
 */

package org.y20k.transistor;

import android.app.NotificationManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.widget.Toast;

import org.y20k.transistor.helpers.NotificationHelper;

import java.io.IOException;

/**
 * PlayerService class
 */
public final class PlayerService extends Service implements AudioManager.OnAudioFocusChangeListener,
        MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener, MediaPlayer.OnInfoListener {

    /* Define log tag */
    private static final String LOG_TAG = PlayerService.class.getSimpleName();

    /* Keys */
    private static final String ACTION_PLAY = "org.y20k.transistor.action.PLAY";
    private static final String ACTION_STOP = "org.y20k.transistor.action.STOP";
    private static final String ACTION_PLAYBACK_STARTED = "org.y20k.transistor.action.PLAYBACK_STARTED";
    private static final String ACTION_PLAYBACK_STOPPED = "org.y20k.transistor.action.PLAYBACK_STOPPED";
    private static final String EXTRA_STREAM_URL = "STREAM_URL";
    private static final String PLAYBACK = "playback";
    private static final int PLAYER_SERVICE_NOTIFICATION_ID = 1;

    /* Main class variables */
    private AudioManager mAudioManager;
    private MediaPlayer mMediaPlayer;
    private String mStreamURL;
    private boolean mPlayback;
    private HeadphoneUnplugReceiver mHeadphoneUnplugReceiver;

    /* Constructor (default) */
    public PlayerService() {
    }

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

        // set up variables
        mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        mMediaPlayer = null;

        // Listen for headphone unplug
        IntentFilter headphoneUnplugIntentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
        mHeadphoneUnplugReceiver = new HeadphoneUnplugReceiver();
        registerReceiver(mHeadphoneUnplugReceiver, headphoneUnplugIntentFilter);

        // TODO Listen for headphone button
        // Use MediaSession
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        // checking for empty intent
        if (intent == null) {
            Log.v(LOG_TAG, "Null-Intent received. Stopping self.");
            stopSelf();
        }

        // ACTION PLAY
        else if (intent.getAction().equals(ACTION_PLAY)) {
            Log.v(LOG_TAG, "Service received command: PLAY");

            // set mPlayback true
            mPlayback = true;

            // get URL of station from intent
            mStreamURL = intent.getStringExtra(EXTRA_STREAM_URL);

            // start playback
            startPlayback();
        }

        // ACTION STOP
        else if (intent.getAction().equals(ACTION_STOP)) {
            Log.v(LOG_TAG, "Service received command: STOP");

            // set mPlayback false
            mPlayback = false;

            // stop playback
            stopPlayback();
        }

        // default return value for media playback
        return START_STICKY;
    }

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

    @Override
    public void onAudioFocusChange(int focusChange) {
        switch (focusChange) {
        // gain of audio focus of unknown duration
        case AudioManager.AUDIOFOCUS_GAIN:
            if (mPlayback) {
                if (mMediaPlayer == null) {
                    initializeMediaPlayer();
                } else if (!mMediaPlayer.isPlaying()) {
                    mMediaPlayer.start();
                }
                mMediaPlayer.setVolume(1.0f, 1.0f);
            }
            break;
        // loss of audio focus of unknown duration
        case AudioManager.AUDIOFOCUS_LOSS:
            stopPlayback();
            break;
        // transient loss of audio focus
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
            if (!mPlayback && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                stopPlayback();
            } else if (mPlayback && mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                mMediaPlayer.pause();
            }
            break;
        // temporary external request of audio focus
        case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
            if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
                mMediaPlayer.setVolume(0.1f, 0.1f);
            }
            break;
        }
    }

    @Override
    public void onPrepared(MediaPlayer mp) {
        mp.start();
    }

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {

        switch (what) {
        case MediaPlayer.MEDIA_ERROR_UNKNOWN:
            Log.e(LOG_TAG, "Unknown media playback error");
            break;
        case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
            Log.e(LOG_TAG, "Connection to server lost");
            break;
        default:
            Log.e(LOG_TAG, "Generic audio playback error");
            break;
        }

        switch (extra) {
        case MediaPlayer.MEDIA_ERROR_IO:
            Log.e(LOG_TAG, "IO media error.");
            break;
        case MediaPlayer.MEDIA_ERROR_MALFORMED:
            Log.e(LOG_TAG, "Malformed media.");
            break;
        case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
            Log.e(LOG_TAG, "Unsupported content type");
            break;
        case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
            Log.e(LOG_TAG, "Media timeout");
            break;
        default:
            Log.e(LOG_TAG, "Other case of media playback error");
            break;
        }

        mp.reset();

        return true;
    }

    @Override
    public boolean onInfo(MediaPlayer mp, int what, int extra) {

        switch (what) {
        case MediaPlayer.MEDIA_INFO_UNKNOWN:
            Log.i(LOG_TAG, "Unknown media info");
            break;
        case MediaPlayer.MEDIA_INFO_BUFFERING_START:
            Log.i(LOG_TAG, "Buffering started");
            break;
        case MediaPlayer.MEDIA_INFO_BUFFERING_END:
            Log.i(LOG_TAG, "Buffering finished");
            break;
        case MediaPlayer.MEDIA_INFO_METADATA_UPDATE:
            Log.i(LOG_TAG, "New metadata available");
            break;
        default:
            Log.i(LOG_TAG, "other case of media info");
            break;
        }

        return true;
    }

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

        // save state
        mPlayback = false;
        savePlaybackState();

        // unregister receivers
        this.unregisterReceiver(mHeadphoneUnplugReceiver);

        // retrieve notification system service and cancel notification
        NotificationManager notificationManager = (NotificationManager) getApplication()
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(PLAYER_SERVICE_NOTIFICATION_ID);

    }

    /* Method to start the player */
    public void startActionPlay(Context context, String streamURL, String stationName) {
        mStreamURL = streamURL;
        Log.v(LOG_TAG, "starting playback service: " + mStreamURL);

        // start player service using intent
        Intent intent = new Intent(context, PlayerService.class);
        intent.setAction(ACTION_PLAY);
        intent.putExtra(EXTRA_STREAM_URL, mStreamURL);
        context.startService(intent);

        // put up notification
        NotificationHelper notificationHelper = new NotificationHelper(context);
        notificationHelper.setStationName(stationName);
        notificationHelper.createNotification();
    }

    /* Method to stop the player */
    public void startActionStop(Context context) {
        Log.v(LOG_TAG, "stopping playback service:");

        // stop player service using intent
        Intent intent = new Intent(context, PlayerService.class);
        intent.setAction(ACTION_STOP);
        context.startService(intent);
    }

    /* Set up the media player */
    private void initializeMediaPlayer() {
        mMediaPlayer = new MediaPlayer();
        mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnErrorListener(this);
        mMediaPlayer.setOnInfoListener(this);
        mMediaPlayer.setOnBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {
            @Override
            public void onBufferingUpdate(MediaPlayer mp, int percent) {
                Log.v(LOG_TAG, "Buffering: " + percent);
            }
        });

        try {
            mMediaPlayer.setDataSource(mStreamURL);
            mMediaPlayer.prepareAsync();
            Log.v(LOG_TAG, "setting: " + mStreamURL);
        } catch (IllegalArgumentException | IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /* Release the media player */
    private void releaseMediaPlayer() {
        if (mMediaPlayer != null) {
            if (mMediaPlayer.isPlaying()) {
                mMediaPlayer.stop();
            }
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }

    }

    /* Start playback */
    private void startPlayback() {
        // stop running player
        if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
            releaseMediaPlayer();
        }

        // request focus and initialize media player
        if (mStreamURL != null && requestFocus()) {
            initializeMediaPlayer();
        }

        // save state
        mPlayback = true;
        savePlaybackState();

        // send local broadcast (needed by MainActivityFragment)
        Intent i = new Intent();
        i.setAction(ACTION_PLAYBACK_STARTED);
        LocalBroadcastManager.getInstance(this.getApplication()).sendBroadcast(i);
    }

    /* Stop playback */
    private void stopPlayback() {
        // release player
        releaseMediaPlayer();

        // save state
        mPlayback = false;
        savePlaybackState();

        // send local broadcast (needed by PlayerActivityFragment and MainActivityFragment)
        Intent i = new Intent();
        i.setAction(ACTION_PLAYBACK_STOPPED);
        LocalBroadcastManager.getInstance(this.getApplication()).sendBroadcast(i);

        // retrieve notification system service and cancel notification
        NotificationManager notificationManager = (NotificationManager) getApplication()
                .getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(PLAYER_SERVICE_NOTIFICATION_ID);
    }

    /* Request audio manager focus */
    private boolean requestFocus() {
        int result = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
        return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
    }

    //    /* Abandon audio manager focus */
    //    private boolean abandonFocus() {
    //        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED ==
    //                mAudioManager.abandonAudioFocus(this);
    //    }

    /* Saves state of playback */
    private void savePlaybackState() {
        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getApplication());
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean(PLAYBACK, mPlayback);
        editor.apply();
    }

    /**
     * Inner class: Receiver for headphone unplug-signal
     */
    public class HeadphoneUnplugReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (mPlayback && AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
                Log.v(LOG_TAG, "Headphones unplugged. Stopping playback.");
                // stop playback
                stopPlayback();
                // notify user
                Toast.makeText(context, R.string.toastalert_headphones_unplugged, Toast.LENGTH_LONG).show();
            }
        }
    }
    /**
     * End of inner class
     */

}