Joe Joe - 25 days ago 9
Android Question

Music Controls in Notification

Following [this tutorial] (http://www.tutorialsface.com/2015/08/android-custom-notification-tutorial) I ended up with the controls showing in the notification, but all are unresponsive (other than showing the Toasts the author included).

The controls don't seem to be bound to the music service in any way. I've tried creating a broadcast, calling the service directly from within the notifications, creating a new instance of the service. I've tried so many things I can't even remember all of them. I've seen people ask similar questions but nothing I've tried works. Now I've tried so many things that I've gotten myself confused about when/where I thought I was on a 'right path'. Any ideas as to how to get these controls functioning properly?

Notification Service:

public class NotificationService extends Service {


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


@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {


if (intent.getAction().equals(Constant.ACTION.STARTFOREGROUND_ACTION)) {
showNotification();
Toast.makeText(this, "Service Started", Toast.LENGTH_SHORT).show();



} else if (intent.getAction().equals(Constant.ACTION.PREV_ACTION)) {
Toast.makeText(this, "Clicked Previous", Toast.LENGTH_SHORT).show();
Log.i("NS", "Clicked Previous");



} else if (intent.getAction().equals(Constant.ACTION.PLAY_ACTION)) {



Log.i("NS", "Clicked Play");
} else if (intent.getAction().equals(Constant.ACTION.NEXT_ACTION)) {





Toast.makeText(this, "Clicked Next", Toast.LENGTH_SHORT).show();
Log.i("NS", "Clicked Next");
} else if (intent.getAction().equals(
Constant.ACTION.STOPFOREGROUND_ACTION)) {
Log.i("NS", "Received Stop Foreground Intent");
Toast.makeText(this, "Service Stoped", Toast.LENGTH_SHORT).show();
stopForeground(true);
stopSelf();
}
return START_STICKY;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void showNotification() {

Notification status;

// Using RemoteViews to bind custom layouts into Notification
RemoteViews views = new RemoteViews(getPackageName(),
R.layout.status_bar);
RemoteViews bigViews = new RemoteViews(getPackageName(),
R.layout.status_bar_expanded);

// showing default album image
views.setViewVisibility(R.id.status_bar_icon, View.VISIBLE);
views.setViewVisibility(R.id.status_bar_album_art, View.GONE);
bigViews.setImageViewBitmap(R.id.status_bar_album_art,
Constant.getDefaultAlbumArt(this));

Intent notificationIntent = new Intent(this, NotificationService.class);
notificationIntent.setAction(Constant.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);

Intent previousIntent = new Intent(this, NotificationService.class);
previousIntent.setAction(Constant.ACTION.PREV_ACTION);
PendingIntent ppreviousIntent = PendingIntent.getService(this, 0,
previousIntent, 0);

Intent playIntent = new Intent(this, NotificationService.class);
playIntent.setAction(Constant.ACTION.PLAY_ACTION);
PendingIntent pplayIntent = PendingIntent.getService(this, 0,
playIntent, 0);

Intent nextIntent = new Intent(this, NotificationService.class);
nextIntent.setAction(Constant.ACTION.NEXT_ACTION);
PendingIntent pnextIntent = PendingIntent.getService(this, 0,
nextIntent, 0);

Intent closeIntent = new Intent(this, NotificationService.class);
closeIntent.setAction(Constant.ACTION.STOPFOREGROUND_ACTION);
PendingIntent pcloseIntent = PendingIntent.getService(this, 0,
closeIntent, 0);

views.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_play, pplayIntent);

views.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_next, pnextIntent);

views.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_prev, ppreviousIntent);

views.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);
bigViews.setOnClickPendingIntent(R.id.status_bar_collapse, pcloseIntent);

views.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);
bigViews.setImageViewResource(R.id.status_bar_play,
R.drawable.apollo_holo_dark_pause);

views.setTextViewText(R.id.status_bar_track_name, "Song Title");
bigViews.setTextViewText(R.id.status_bar_track_name, "Song Title");

views.setTextViewText(R.id.status_bar_artist_name, "Artist Name");
bigViews.setTextViewText(R.id.status_bar_artist_name, "Artist Name");

bigViews.setTextViewText(R.id.status_bar_album_name, "Album Name");

status = new Notification.Builder(this).build();
status.contentView = views;
status.bigContentView = bigViews;
status.flags = Notification.FLAG_ONGOING_EVENT;
status.icon = R.drawable.ic_launcher;
status.contentIntent = pendingIntent;
startForeground(Constant.NOTIFICATION_ID.FOREGROUND_SERVICE, status);
}

}


MusicService:

public class MusicService extends Service implements
MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener {

//media player
private MediaPlayer player;
//song list
private ArrayList<Song> songs;
//current position
private int songPosn;
private final IBinder musicBind = new MusicBinder();
private String songTitle="";
private static final int NOTIFY_ID=1;
private boolean shuffle=false;
private Random rand;

public void onCreate(){
//create the service
super.onCreate();
songPosn=0;
player = new MediaPlayer();

initMusicPlayer();

rand=new Random();
}

public void initMusicPlayer(){
//set player properties
player.setWakeMode(getApplicationContext(),
PowerManager.PARTIAL_WAKE_LOCK);
player.setAudioStreamType(AudioManager.STREAM_MUSIC);

player.setOnPreparedListener(this);
player.setOnCompletionListener(this);
player.setOnErrorListener(this);
}

public void setList(ArrayList<Song> theSongs){
songs=theSongs;
}

public class MusicBinder extends Binder {
MusicService getService() {
return MusicService.this;
}
}

public void setShuffle(){
if(shuffle) shuffle=false;
else shuffle=true;
}

public void playSong(){
player.reset();
//get song
Song playSong = songs.get(songPosn);

//get id
long currSong = playSong.getID();
//set uri
Uri trackUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
currSong);

try{
player.setDataSource(getApplicationContext(), trackUri);
}
catch(Exception e){
Log.e("MUSIC SERVICE", "Error setting data source", e);
}

player.prepareAsync();

songTitle=playSong.getTitle();
}

public void setSong(int songIndex){
songPosn=songIndex;
}

public int getPosn(){
return player.getCurrentPosition();
}

public int getDur(){
return player.getDuration();
}

public boolean isPng(){
return player.isPlaying();
}

public void pausePlayer(){
player.pause();
}

public void seek(int posn){
player.seekTo(posn);
}

public void go(){
player.start();
}

public void playPrev(){
songPosn--;
if(songPosn<0) songPosn=songs.size()-1;
playSong();
}

//skip to next
public void playNext(){
if(shuffle){
int newSong = songPosn;
while(newSong==songPosn){
newSong=rand.nextInt(songs.size());
}
songPosn=newSong;
}
else{
songPosn++;
if(songPosn>=songs.size()) songPosn=0;
}
playSong();
}
@Override
public void onDestroy() {
stopForeground(true);
}

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

@Override
public boolean onUnbind(Intent intent){
player.stop();
player.release();
return false;
}

@Override
public void onCompletion(MediaPlayer mp) {
if(player.getCurrentPosition()>0){
mp.reset();
playNext();
}
}

@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
mp.reset();
return false;
}

@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onPrepared(MediaPlayer mp) {
//start playback
mp.start();

Intent onPreparedIntent = new Intent("MEDIA_PLAYER_PREPARED");
LocalBroadcastManager.getInstance(this).sendBroadcast(onPreparedIntent);


Intent notIntent = new Intent(this, MusicPlayer.class);
notIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendInt = PendingIntent.getActivity(this, 0,
notIntent, PendingIntent.FLAG_UPDATE_CURRENT);

Notification.Builder builder = new Notification.Builder(this);

builder.setContentIntent(pendInt)
.setSmallIcon(R.drawable.player_play)
.setTicker(songTitle)
.setOngoing(true)
.setContentTitle("Playing")
.setContentText(songTitle);
Notification not = builder.build();

Intent serviceIntent = new Intent(MusicService.this, NotificationService.class);
serviceIntent.setAction(Constant.ACTION.STARTFOREGROUND_ACTION);
startService(serviceIntent);
}
}


Music Player:

public class MusicPlayer extends AppCompatActivity implements MediaPlayerControl {

private ArrayList<Song> songList;
private ListView songView;
public MusicService musicSrv;
private Intent playIntent;
private boolean musicBound=false;
private MusicController controller;
private boolean paused=false, playbackPaused=false;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_music_player);


songView = (ListView)findViewById(R.id.song_list);
songList = new ArrayList<Song>();

getSongList();

Collections.sort(songList, new Comparator<Song>(){
public int compare(Song a, Song b){
return a.getArtist().compareTo(b.getArtist());
}
});

songAdapter songAdt = new songAdapter(this, songList);
songView.setAdapter(songAdt);

setController();

FloatingActionButton fabRandom = (FloatingActionButton)findViewById(R.id.fabRandom);
fabRandom.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

}
});

}

//play next
private void playNext(){
musicSrv.playNext();
if(playbackPaused){
setController();
playbackPaused=false;
}
controller.show(0);
}

private void playPrev(){
musicSrv.playPrev();
if(playbackPaused){
setController();
playbackPaused=false;
}
controller.show(0);
}

public void songPicked(View view){
musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
musicSrv.playSong();
if(playbackPaused){
setController();
playbackPaused=false;
}
setController();
controller.show(0);
}
@Override

protected void onPause(){
super.onPause();
paused=true;
}

@Override
protected void onResume(){
super.onResume();

// Set up receiver for media player onPrepared broadcast
LocalBroadcastManager.getInstance(this).registerReceiver(onPrepareReceiver,
new IntentFilter("MEDIA_PLAYER_PREPARED"));


if(paused){
setController();
paused=false;
}
}

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

private void setController(){
//set the controller up


if (controller == null) controller = new MusicController(this);

controller.invalidate();


controller.setPrevNextListeners(new View.OnClickListener() {
@Override
public void onClick(View v) {
playNext();
}
}, new View.OnClickListener() {
@Override
public void onClick(View v) {
playPrev();
}
});

controller.setMediaPlayer(this);
controller.setAnchorView(findViewById(R.id.song_list));

}

@Override

public void onBackPressed() {



moveTaskToBack(true);
Intent intent = new Intent (getApplicationContext(), FitnessActivity.class);
startActivity(intent);



}


// Broadcast receiver to determine when music player has been prepared
private BroadcastReceiver onPrepareReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context c, Intent i) {
// When music player has been prepared, show controller
controller.show(0);
}
};

private BroadcastReceiver onNextClickedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {

musicSrv.playNext();

}
};

//connect to the service
public ServiceConnection musicConnection = new ServiceConnection(){

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
MusicService.MusicBinder binder = (MusicService.MusicBinder)service;
//get service
musicSrv = binder.getService();
//pass list
musicSrv.setList(songList);
musicBound = true;
}

@Override
public void onServiceDisconnected(ComponentName name) {
musicBound = false;
}
};

@Override
protected void onStart() {
super.onStart();
if(playIntent==null){
playIntent = new Intent(this, MusicService.class);
bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
startService(playIntent);
setController();
}
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_shuffle:
musicSrv.setShuffle();
break;
case R.id.action_end:
stopService(playIntent);
musicSrv=null;
System.exit(0);
break;
}
return super.onOptionsItemSelected(item);
}

@Override
protected void onDestroy() {
stopService(playIntent);
musicSrv=null;
super.onDestroy();
}

public void getSongList() {
//retrieve song info
ContentResolver musicResolver = getContentResolver();
Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);

if(musicCursor!=null && musicCursor.moveToFirst()){
//get columns
int titleColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media._ID);
int artistColumn = musicCursor.getColumnIndex
(android.provider.MediaStore.Audio.Media.ARTIST);
int durationColumn = musicCursor.getColumnIndex
(MediaStore.Audio.Media.DURATION);
//add songs to list


do {
long thisId = musicCursor.getLong(idColumn);
String thisTitle = musicCursor.getString(titleColumn);
String thisArtist = musicCursor.getString(artistColumn);
long thisDuration = musicCursor.getLong(durationColumn);
if (thisDuration>10000){
songList.add(new Song(thisId, thisTitle, thisArtist, thisDuration));}
}
while (musicCursor.moveToNext());
}
}


@Override
public void start() {
musicSrv.go();
}

@Override
public void pause() {
playbackPaused=true;
musicSrv.pausePlayer();
}

@Override
public int getDuration() {
if(musicSrv!=null && musicBound && musicSrv.isPng())
return musicSrv.getDur();
else return 0;
}

@Override
public int getCurrentPosition() {
if(musicSrv!=null && musicBound && musicSrv.isPng())
return musicSrv.getPosn();
else return 0;
}

@Override
public void seekTo(int pos) {
musicSrv.seek(pos);
}

@Override
public boolean isPlaying() {
if(musicSrv!=null && musicBound)
return musicSrv.isPng();
return false;
}

@Override
public int getBufferPercentage() {
return 0;
}

@Override
public boolean canPause() {
return true;
}

@Override
public boolean canSeekBackward() {
return true;
}

@Override
public boolean canSeekForward() {
return true;
}

@Override
public int getAudioSessionId() {
return 0;
}

public void onNextNotification(){

}
}


Manifest:

<activity
android:name=".MusicPlayer"
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".MusicService"
/>
<service android:name=".NotificationService" />

Answer

Your notification actions gets received by Notifications service, not the Music service. Edit notification intents to target Music Service

Intent playIntent = new Intent(this, MusicService.class);
//... edit all of them

Move onStartCommand() method to Music Service and assign actions

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

    if (intent.getAction().equals(Constant.ACTION.PLAY_ACTION)) {
        //call play method
    }
//...etc

except STARTFOREGROUND_ACTION and STOPFOREGROUND_ACTION, leave it in Notification Service's onStartCommand(). Also insert null check on Intent in both services.