package it.fdev.unisaconnect; import it.fdev.unisaconnect.R; import it.fdev.utils.DrawableManager; import it.fdev.utils.Utils; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.util.Collection; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import org.jsoup.Connection.Response; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnInfoListener; import android.media.MediaPlayer.OnPreparedListener; import android.os.IBinder; import android.os.PowerManager; import android.support.v4.app.NotificationCompat; import android.util.Log; import android.widget.RemoteViews; import de.umass.lastfm.Caller; import de.umass.lastfm.ImageSize; import de.umass.lastfm.Track; public class WebRadioPlayerService extends Service implements OnCompletionListener, OnPreparedListener, OnErrorListener, AudioManager.OnAudioFocusChangeListener, OnInfoListener { private static final String ICECAST_URL = "http://streamingradio.unisa.it/status.xsl?mount=/stream"; private static final String LASTFM_API_KEY = "5d28b9b80d89cebd38f1f604e5ddf01d"; public static final String BROADCAST_STATUS_CHANGED = "it.fdev.webradio.STATUS_CHANGED"; public static final String ACTION_PLAY = "it.fdev.webradio.PLAY"; public static final String ACTION_STOP = "it.fdev.webradio.STOP"; public static final String ACTION_UPDATE = "it.fdev.webradio.UPDATE"; public static final int NOTIFICATION_ID = 1; public static final int INTERVAL_FETCH_METADATA = 15 * 1000; // Milliseconds private static MediaPlayer mMediaPlayer = null; private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); private ScheduledFuture<?> matedataRetrieverHandle; private NotificationManager mNotificationManager = null; private NotificationCompat.Builder mNotifyBuilder = null; private RemoteViews mNotifyContentView = null; private boolean isBuffering = false; private String lastMetadata; private String mSongTitle, mSongArtist, mAlbumArtFileName; private Bitmap mAlbumArt; @Override public int onStartCommand(Intent intent, int flags, int startId) { if (mNotificationManager == null) { if (intent == null) { return super.onStartCommand(intent, flags, startId); } if (!intent.getAction().equals(ACTION_PLAY)) { stopService(); return super.onStartCommand(intent, flags, startId); } Intent intent_notification = new Intent(this, MainActivity.class); intent_notification.setAction(MainActivity.INTENT_LAUNCH_FRAGMENT); intent_notification.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP); intent_notification.putExtra("launch_fragment", MainActivity.BootableFragmentsEnum.WEB_RADIO); PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent_notification, PendingIntent.FLAG_UPDATE_CURRENT); mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotifyContentView = new RemoteViews(getPackageName(), R.layout.web_radio_notification); mNotifyContentView.setTextViewText(R.id.song_name, "Title"); mNotifyContentView.setTextViewText(R.id.song_artist, "Artist"); CloseButtonListener closeButtonListener = new CloseButtonListener(); registerReceiver(closeButtonListener, new IntentFilter("it.fdev.unisaconnect.close_listener")); Intent switchIntent = new Intent("it.fdev.unisaconnect.close_listener"); PendingIntent pendingSwitchIntent = PendingIntent.getBroadcast(this, 0, switchIntent, PendingIntent.FLAG_UPDATE_CURRENT); mNotifyContentView.setOnClickPendingIntent(R.id.button_stop, pendingSwitchIntent); mNotifyBuilder = new NotificationCompat.Builder(this).setContentTitle("Title").setContentText("Artist").setAutoCancel(false).setSmallIcon(R.drawable.ic_stat_music).setContentIntent(pIntent).setOngoing(true).setWhen(0).setContent(mNotifyContentView); } if (intent == null || intent.getAction() == null) { return super.onStartCommand(intent, flags, startId); } if (intent.getAction().equals(ACTION_PLAY)) { if (mMediaPlayer != null) { // Already playing return START_STICKY; // return super.onStartCommand(intent, flags, startId); } AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { return START_STICKY; } isBuffering = true; sendStatusBroadcast(); try { final String streamingURL = intent.getStringExtra("streamingURL"); mMediaPlayer = new MediaPlayer(); mMediaPlayer.setDataSource(streamingURL); mMediaPlayer.setOnCompletionListener(this); mMediaPlayer.setOnPreparedListener(this); mMediaPlayer.setOnErrorListener(this); mMediaPlayer.setOnInfoListener(this); mMediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK); mMediaPlayer.prepareAsync(); isBuffering = true; Runnable retrieverTask = new Runnable() { public void run() { try { retrieveMetadata(); // retrieveMetadata(streamingURL); } catch (Exception e) { Log.e(Utils.TAG, "Exception in WebRadioPlayerService", e); } }; }; matedataRetrieverHandle = scheduler.scheduleWithFixedDelay(retrieverTask, 0, INTERVAL_FETCH_METADATA, TimeUnit.MILLISECONDS); return START_STICKY; } catch (Exception e) { Log.e(Utils.TAG, "Exception in WebRadioPlayerService", e); stopService(); } } else if (intent.getAction().equals(ACTION_STOP)) { stopService(); } else if (intent.getAction().equals(ACTION_UPDATE)) { sendStatusBroadcast(); return START_STICKY; } return super.onStartCommand(intent, flags, startId); } private void stopService() { if (mNotificationManager != null) { mNotificationManager.cancelAll(); } if (mMediaPlayer != null) { mMediaPlayer.stop(); mMediaPlayer.release(); mMediaPlayer = null; } if (matedataRetrieverHandle != null) { scheduler.shutdownNow(); matedataRetrieverHandle.cancel(true); matedataRetrieverHandle = null; } isBuffering = false; sendStatusBroadcast(); Log.d("Player Service", "Player Service Stopped"); stopForeground(true); stopSelf(); } // Create Notification private void updateNotification() { if (mMediaPlayer==null || !mMediaPlayer.isPlaying()) { return; } if (mSongTitle == null) { mSongTitle = getString(R.string.unisound); } mNotifyContentView.setTextViewText(R.id.song_name, mSongTitle); mNotifyContentView.setTextViewText(R.id.song_artist, mSongArtist); if (mAlbumArt == null) { mNotifyContentView.setImageViewResource(R.id.song_album_art, R.drawable.music_note); } else { mNotifyContentView.setImageViewBitmap(R.id.song_album_art, mAlbumArt); } mNotifyBuilder.setContentTitle(mSongTitle).setContentText(mSongArtist); mNotificationManager.notify(NOTIFICATION_ID, mNotifyBuilder.build()); } private void sendStatusBroadcast() { boolean isPlaying = false; if (mMediaPlayer != null) { isPlaying = mMediaPlayer.isPlaying(); } if (mSongTitle == null) { mSongTitle = getString(R.string.unisound); } Intent intent = new Intent(BROADCAST_STATUS_CHANGED); intent.putExtra("is_playing", isPlaying); intent.putExtra("is_buffering", isBuffering); intent.putExtra("title", mSongTitle); intent.putExtra("artist", mSongArtist); intent.putExtra("imageFile", mAlbumArtFileName); getApplicationContext().sendBroadcast(intent); } /** * Listeners */ @Override public void onDestroy() { stopService(); super.onDestroy(); } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCompletion(MediaPlayer mp) { Log.d(Utils.TAG, "WebRadioPlayerService | onCompletition"); isBuffering = false; stopService(); sendStatusBroadcast(); } @Override public void onPrepared(MediaPlayer mp) { Log.d(Utils.TAG, "WebRadioPlayerService | onPrepared"); isBuffering = false; mp.start(); updateNotification(); sendStatusBroadcast(); } @Override public boolean onInfo(MediaPlayer mp, int what, int extra) { Log.d(Utils.TAG, "WebRadioPlayerService | onInfo: " + what); if (what == MediaPlayer.MEDIA_INFO_BUFFERING_START) { isBuffering = true; mp.pause(); } else if (what == MediaPlayer.MEDIA_INFO_BUFFERING_END) { isBuffering = false; mp.start(); } updateNotification(); sendStatusBroadcast(); return false; } @Override public boolean onError(MediaPlayer mp, int what, int extra) { Log.d(Utils.TAG, "WebRadioPlayerService | onError: " + what); stopService(); return false; } // http://developer.android.com/guide/topics/media/mediaplayer.html @Override public void onAudioFocusChange(int focusChange) { Log.d(Utils.TAG, "WebRadioPlayerService | onAudioFocusChange: " + focusChange); switch (focusChange) { case AudioManager.AUDIOFOCUS_GAIN: // resume playback if (mMediaPlayer == null) return; else if (!mMediaPlayer.isPlaying()) mMediaPlayer.start(); 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 && mMediaPlayer.isPlaying()) stopService(); 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 && mMediaPlayer.isPlaying()) { isBuffering = false; mMediaPlayer.pause(); } 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.isPlaying()) { // isBuffering = false; // mMediaPlayer.pause(); // } if (mMediaPlayer!=null && mMediaPlayer.isPlaying()) mMediaPlayer.setVolume(0.2f, 0.2f); break; } updateNotification(); sendStatusBroadcast(); } public void fetchAndSaveAlbumArt() { mAlbumArt = null; mAlbumArtFileName = null; try { Caller.getInstance().setCache(null); Collection<Track> trcks = Track.search(mSongArtist, mSongTitle, 1, LASTFM_API_KEY); if (trcks.size() <= 0) { return; } Track mTrack = (Track) trcks.toArray()[0]; String albumArtURL = mTrack.getImageURL(ImageSize.LARGE); Drawable image = new DrawableManager().fetchDrawable(albumArtURL); mAlbumArt = ((BitmapDrawable) image).getBitmap(); saveAlbumArt(); return; } catch (Exception e) { Log.e(Utils.TAG, "Exception in WebRadioPlayerService", e); mAlbumArt = null; return; } } public void saveAlbumArt() { String fileName = "current_album_art.png"; if (mAlbumArt == null) { mAlbumArtFileName = null; return; } try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); mAlbumArt.compress(Bitmap.CompressFormat.PNG, 100, baos); byte[] b = baos.toByteArray(); FileOutputStream fileOutStream = openFileOutput(fileName, MODE_PRIVATE); fileOutStream.write(b); fileOutStream.close(); } catch (Exception e) { Log.e(Utils.TAG, "Exception in WebRadioPlayerService", e); mAlbumArtFileName = null; return; } mAlbumArtFileName = fileName; return; } public class CloseButtonListener extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Intent playerService = new Intent(context, WebRadioPlayerService.class); playerService.setAction(WebRadioPlayerService.ACTION_STOP); context.startService(playerService); } } private void retrieveMetadata() { try { Response response = Jsoup.connect(ICECAST_URL).timeout(30000).execute(); Document document = response.parse(); String metadata = document.getElementsContainingOwnText("Current Song").first().nextElementSibling().text(); if (metadata.equalsIgnoreCase(lastMetadata)) return; lastMetadata = metadata; mAlbumArt = null; mAlbumArtFileName = null; metadata = metadata.replaceAll("^UniS@und - ", ""); if (metadata.contains("-")) { int indexSep = metadata.indexOf("-"); mSongArtist = metadata.substring(0, indexSep).trim(); mSongTitle = metadata.substring(indexSep + 1).trim(); } else { mSongTitle = metadata; mSongArtist = null; mAlbumArt = null; } } catch (Exception e) { Log.e(Utils.TAG, "Exception in WebRadioPlayerService", e); mSongTitle = null; mSongArtist = null; mAlbumArt = null; mAlbumArtFileName = null; } if (mSongTitle == null) { // Problema ner reperire il tag. -> imposto una notifica generica mSongArtist = null; mAlbumArt = null; mAlbumArtFileName = null; } updateNotification(); sendStatusBroadcast(); if (mSongTitle != null && mSongArtist != null) { fetchAndSaveAlbumArt(); updateNotification(); sendStatusBroadcast(); } } }