Skip to content

Instantly share code, notes, and snippets.

@daniel-c05
Last active December 14, 2015 03:49
Show Gist options
  • Save daniel-c05/308d878b5539933f6541 to your computer and use it in GitHub Desktop.
Save daniel-c05/308d878b5539933f6541 to your computer and use it in GitHub Desktop.
Some bits of code on a Music playback service, and on an Application class that helps ACRA logs.
@ReportsCrashes(formKey = "myFormId...",
mode = ReportingInteractionMode.TOAST,
mailTo = "my.email@gmail.com",
resToastText = R.string.app_crashed_prompt
)
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
// The following line triggers the initialization of ACRA
ACRA.init(this);
}
}
public class MyMusicService extends Service {
@Override
public void onCreate () {
Log.v(HomeActivity.TAG, "Service Created");
mContext = getApplicationContext();
IntentFilter commandFilter = new IntentFilter();
commandFilter.addAction(ACTION_PLAY);
//..May other filters
registerReceiver(mReceiver, commandFilter);
mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
if(mgr != null) {
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
curQueue = new ArrayList<Long>();
restoreQueue();
initiatePlayer(); //Here we do not call prepare yet
}
@Override
public void onDestroy () {
Log.v(HomeActivity.TAG, "Service Destroyed");
//Save the songs on queue
saveQueue();
if (mCursor != null) {
//close the cursor if not null
mCursor.close();
}
//cancel notification if any
mNotificationManager.cancel(mId);
//unregister the broadcast receiver
unregisterReceiver(mReceiver);
//stop listening to phone state
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v(HomeActivity.TAG, "Received start id " + startId + ": " + intent);
return START_STICKY;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
if (mPlayer != null && mPlayer.isPlaying()) {
mPlayer.pause();
}
}
else if (action.equals(ACTION_PLAYLIST)) {
changePlaylist(intent.getLongExtra(EXTRA_PLAYLIST_ID, 0));
}
else if (action.equals(ACTION_DISPLAY_PENDING_NOTIFICATION)) {
if (isPlaying()) {
Log.v(HomeActivity.TAG, "Attempting to show notification");
showNotifaction = true;
showNotification();
}
}
else if (action.equals(ACTION_CANCEL_NOTIFICATION)) {
Log.v(HomeActivity.TAG, "Attempting to cancel notification");
showNotifaction = false;
stopForeground(true); //This somehow doesn't cancel the notification
mNotificationManager.cancel(mId); //This has to be called too
}
else if (action.equals(ACTION_ENQUEUE)) {
final long [] songs = intent.getLongArrayExtra(EXTRA_QUEUE_SONGS);
final boolean play = intent.getBooleanExtra(EXTRA_QUEUE_PLAY, false);
updateCurQueue(songs, play);
}
else if (action.equals(ACTION_CLEAR_QUEUE)) {
clearQueue();
}
else if (action.equals(ACTION_NEXT)) {
playNextSong(true, intent.getBooleanExtra(EXTRA_BOOL_FORCE, true));
}
else if (action.equals(ACTION_PREVIOUS)) {
playPreviousSong(intent.getBooleanExtra(EXTRA_BOOL_FORCE, true));
}
else if (action.equals(ACTION_STOP)) {
stopPlayBack();
}
else if (action.equals(ACTION_PLAY_PAUSE)) {
playOrPause(intent.getBooleanExtra(EXTRA_BOOL_FORCE, true));
}
else if (action.equals(ACTION_PLAY)) {
curSongId = intent.getExtras().getLong("_id");
if (curQueue.contains(curSongId)) {
mCurPosition = curQueue.indexOf(curSongId);
}
else {
mCurPosition = 0;
}
playSong(curSongId, intent.getBooleanExtra(EXTRA_NOTIFY, false));
}
}
};
protected void playSong(long songId, boolean show) {
if (!curQueue.contains(songId)) {
curQueue.add(songId);
notifyQueueChanged();
}
mCurPosition = curQueue.indexOf(songId);
curSongId = songId;
try {
String selection = Audio.Media._ID + "=" + String.valueOf(songId);
//The cursor I talked about
mCursor = mContext.getContentResolver().query(MusicManager.URI_SONGS, MusicManager.ALL_MEDIA_COLS, selection, null, null);
if (mCursor.moveToFirst()) {
if (mPlayer == null) {
initiatePlayer();
}
mPlayer.reset();
mPlayer.setDataSource(mCursor.getString((mCursor.getColumnIndex(Audio.Media.DATA))));
//I know I should use prepareAsync, bite me lol
mPlayer.prepare();
mPlayer.start();
getCurTrackDetails();
if (show) {
showNotification();
}
}
} catch (Exception e) {
Log.v(HomeActivity.TAG, e.toString());
}
}
protected void playPreviousSong(boolean show) {
if (mPlayer == null) {
return;
}
if (curQueue.size() == 0) {
return;
}
if (mPlayer.getCurrentPosition() < MIN_MILSECS_TO_REPEAT && mCurPosition > 0) {
mCurPosition--;
}
playSong(curQueue.get(mCurPosition), show);
}
protected void playNextSong(boolean force, boolean show) {
if (mPlayer == null) {
return;
}
if (curQueue.size() == 0) {
return;
}
if(mCurRepeatStatus == REPEAT_ON) {
if (!force) {
repeatSong(show);
return;
}
}
else if (mCurRepeatStatus == REPEAT_ALL) {
if (!force) {
repeatQueue(show);
return;
}
}
if (mCurPosition == (curQueue.size() - 1)) {
if (!mPlayer.isPlaying()) {
if (showNotifaction) {
mNotification.contentView.setImageViewResource(R.id.notification_button_play, R.drawable.button_play_holo);
//mNotification.bigContentView.setImageViewResource(R.id.expanded_notification_button_play, R.drawable.ic_play_holo_dark);
mNotificationManager.notify(mId, mNotification);
}
Intent songFinishedIntent = new Intent(ACTION_PLAYBACK_FINISHED);
getApplication().sendBroadcast(songFinishedIntent);
startSelfDestroySequence(KILL_ME_TIMEOUT);
}
else {
repeatQueue(show);
}
return;
}
mCurPosition++;
playSong(curQueue.get(mCurPosition), show);
}
public static void toggleShuffle() {
switch (mCurShuffleStatus) {
case SHUFFLE_OFF:
mCurShuffleStatus = SHUFFLE_ON;
break;
case SHUFFLE_ON:
mCurShuffleStatus = SHUFFLE_OFF;
default:
break;
}
}
public static void toggleRepeat() {
switch (mCurRepeatStatus) {
case REPEAT_OFF:
mCurRepeatStatus = REPEAT_ON;
break;
case REPEAT_ON:
mCurRepeatStatus = REPEAT_ALL;
break;
case REPEAT_ALL:
mCurRepeatStatus = REPEAT_OFF;
break;
default:
break;
}
}
public static boolean isPlaying() {
if (mPlayer != null) {
return mPlayer.isPlaying();
}
return false;
}
/**
* Probably the most memory-demanding method.
*/
@SuppressLint("NewApi")
public void showNotification() {
Log.v(HomeActivity.TAG, "Call to show notification received, building");
if (mCursor.isClosed() || mCursor == null) {
return;
}
if (mPlayer == null) {
return;
}
//Build small notification
RemoteViews views = new RemoteViews(getPackageName(), R.layout.player_notification);
Bitmap bitmap = null;
Drawable d;
Log.v(HomeActivity.TAG, "Current song Id is: " + curSongId);
//Get the cached drawable if any
d = UrlImageViewHelper.getImmediateMutableDrawable(getImageUrl(""+ curSongId));
if (d != null) {
Log.v(HomeActivity.TAG, "Drawable found for: " + curSongId);
//Conver the drawable to a bitmap in order to set the ImageBitmap on the RemoteView
bitmap = ((BitmapDrawable)d).getBitmap();
}
if (bitmap != null) {
Log.v(HomeActivity.TAG, "Updated bitmap");
//This works fine, like I said, crash is while playing the same song,
//not when switching to another.
views.setImageViewBitmap(R.id.notification_default_drawable, bitmap);
}
else {
views.setImageViewResource(R.id.notification_default_drawable, R.drawable.dummy_thumb);
}
Intent mediaButtonIntent = new Intent(ACTION_PLAY_PAUSE);
mediaButtonIntent.putExtra(EXTRA_BOOL_FORCE, true);
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
1, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.notification_button_play, mediaPendingIntent);
mediaButtonIntent = new Intent(ACTION_NEXT);
mediaButtonIntent.putExtra(EXTRA_NOTIFY, true);
mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 2,
mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.notification_button_next, mediaPendingIntent);
mediaButtonIntent = new Intent(ACTION_STOP);
mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 3,
mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.notification_button_exit, mediaPendingIntent);
mediaButtonIntent = new Intent(ACTION_PREVIOUS);
mediaButtonIntent.putExtra(EXTRA_NOTIFY, true);
mediaPendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 4,
mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
int playIconResource;
if (isPlaying()) {
playIconResource = R.drawable.button_pause_holo;
}
else {
playIconResource = R.drawable.button_play_holo;
}
views.setImageViewResource(R.id.notification_button_play, playIconResource);
views.setTextViewText(R.id.notification_song_artist, mCursor.getString(mCursor.getColumnIndex(Audio.Media.ARTIST)));
views.setTextViewText(R.id.notification_song_title, mCursor.getString(mCursor.getColumnIndex(Audio.Media.TITLE)));
mNotification = new Notification();
mNotification.flags = Notification.FLAG_ONGOING_EVENT;
mNotification.icon = R.drawable.ic_launcher;
mNotification.contentIntent = PendingIntent
.getActivity(this, 0, new Intent(this, PlayerHolder.class)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP), 0);
mNotification.contentView = views;
startForeground(mId, mNotification); //Like before, this seems not to "show" notification
mNotificationManager.notify(mId, mNotification); //I have to manually call notify
}
private void initiatePlayer () {
mPlayer = new MediaPlayer();
mPlayer.setOnCompletionListener(new OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
playNextSong(false);
}
});
}
/**
* I use this method every time a song changes to notify the activities listening
* that changes to the UI need to be done.
*/
public static void getCurTrackDetails() {
if (mCursor == null || curQueue.size() == 0) {
return;
}
Bundle bundle = new Bundle();
bundle.putString(PlayerFragment.TRACK_TITLE, mCursor.getString(mCursor.getColumnIndex(Audio.Media.TITLE)));
bundle.putLong(PlayerFragment.TRACK_DURATION, mPlayer.getDuration());
bundle.putString(PlayerFragment.TRACK_ARTIST, mCursor.getString(mCursor.getColumnIndex(Audio.Media.ARTIST)));
bundle.putString(PlayerFragment.TRACK_ART, mCursor.getString(mCursor.getColumnIndex(Audio.Media._ID)));
bundle.putString(PlayerFragment.TRACK_ID, mCursor.getString(mCursor.getColumnIndex(Audio.Media._ID)));
Intent songFinishedIntent = new Intent(ACTION_SONG_CHANGED);
songFinishedIntent.putExtras(bundle);
mContext.sendBroadcast(songFinishedIntent);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment