Java tutorial
/** * 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 */ }