3

I am implementing a music application from this tutorial.

There is a BaseAdapter class used to display the track list, and a MusicPlayer class to play the music. The both are variables of my main activity class.

public class MainActivity extends Activity implements MediaPlayerControl{
       private MediaPlayer musicSrv;
       private BaseAdapter songAdt;
       ...

The MusicPlayer play the next tracks when the current finish. What is the best way to send a message to the BaseAdapter to change the displaying at each new playing track (like changing the color of the current track)?

EDIT

According to the comments, it seems that the use of an interface good be a good option. Could someone write a detail answer that explains how to do it? Thanks.

PatriceG
  • 3,851
  • 5
  • 28
  • 43

4 Answers4

1

Thanks to the comments, I managed to implement a solution with an interface.

This is my Main activity class, that refresh the BaseAdapater each time the song is changed:

public class MainActivity extends Activity implements MusicService.SongChangedListener {
      private MediaPlayer musicSrv;
      private BaseAdapter songAdt;
      ...

      @Override
      public void songChanged(Song currentSong){
           songAdt.notifyDataSetChanged(); // refresh view
      }

And my MusicService class:

public class MusicService extends Service implements MediaPlayer.OnPreparedListener{
...
private MainActivity activity;
...

public void setActivity(MainActivity act){
    //set the activity
    activity = act;
}

public interface SongChangedListener {
     void songChanged(Song song);
}
...
public void playSong(){ 
       // this function is called each time a new song is played
       activity.songChanged(playSong);
       ...
}
PatriceG
  • 3,851
  • 5
  • 28
  • 43
0

BaseAdapter's method, getView() method will provide you with the view and you should change the color of your of current track by setting a variable in your list and reset that color to default when the variable is not set.

if (is this the current playing track) {
    // Set the color of the view.
} else {
    // Set the color to default.
}

If you have implemented this logic currently, then whenever you change the current track and also the variable in your list that tracks the current playing Media, a simple songAdt.notifyDataSetChanged() will ask the BaseAdapter to be called again and will set the view as per the new data. For More indepth understanding of ListView you can refer this talk. It will help.

Preferably consider training yourself with RecyclerView, its the present. ListView was a dreadful past.

public class Activity implements SongChangedListener {
...
    @Override
    onCreate() {
        ....
        PlayerManager pManager = new PlayerManager();
    }

    onResume() {
        pManager.setListener(this);
    }

    onPause() {
        pManager.setListener(null);
    }

    @Override
    void songChanged (MediaId idOfSong) {
         if (getActivity == null)  //If you received a callback after activity was killed.
             return;
        // Change the current song as not playing in List. (for your adapter)
        // Change the idOfSong to currently playing in List (for your adapter).
        // change currentSong = idOfSong;
        // notify that the data in List has changed (songAdt.notifyDataSetChanged)
    }

}

And in Your PlayerManager, you can create the interface, or maybe a seperate class for the interface, doesn't matter how you send the interface instance.

public class PlayerManager {
    ...
    private SongChangedListener mListener;
    ...

    public PlayerManager() {
    }

    public void setListener(SongChangedListener listener) {
        mListener = listener;
    }

    public interface SongChangedListener {
       void songChanged(MediaId idOfSong);
    }

    ...
    public void playSong() { 
       ...
       if (mListener != null)
           mListener.songChanged(idOfNextSong);
       ...
    }

In your answer you are passing an activity into your service, which feels wrong in many ways. If you want to implement communication between activity and service, there are many other ways to do this. Usually I use a Messenger in conjunction with a Handler. I would provide more details but it would be more beneficial if you explore it in documentation and in other answers. It is easy to implement once you understand how Messengers work.

Also, if you are looking for a fullfledged MediaPlayer Application, your implementation will require a lot more boiler code. Also you will have to handle MediaButton clicks(if someone clicked on play/pause on their bluetooth headphones or on their watch). Preferably MediaSessionCompat is a better implementation. You can also refer the following open Source MediaPlayer, which implements all the minimum required functionalities pretty nicely android-UniversalMusicPlayer.

Roadblock
  • 2,041
  • 2
  • 24
  • 38
  • I've got a songAdt.notifyDataSetChanged() in my main activity class. I use it when I change songs manually. The problem comes when the MediaPlayer switch automatically to the next song after completion. I can't call the songAdt.notifyDataSetChanged() in the MediaPlayer class, as songAdt is not known. – PatriceG Jun 30 '16 at 15:01
  • 1
    Try adding callbacks, create an interface in ur media player class and have a method named songChanged(). Implement that interface in ur mainActivity and when u change song in MediaPlayer class call this method, so that song change gets reflected in ur mainActivity. It's a pretty common design used with callbacks to notify activity for changes. – Roadblock Jun 30 '16 at 15:07
  • 1
    U will have to pass an instance of ur mainActivity to MediaPlayer class (Use MediaPlayers constructor). Use this instance to call the songChanged method. – Roadblock Jun 30 '16 at 15:09
  • It looks nice! Could you please show me a piece of code? – PatriceG Jun 30 '16 at 15:10
  • "implement that interface in ur mainActivity " -> Does it mean that the mainActivity has to implement the MediaPlayer class? – PatriceG Jul 01 '16 at 13:40
  • As you was the first to propose to use the interface, I will award you the bounty if you write it as an answer. Thanks – PatriceG Jul 07 '16 at 08:33
  • I have updated the answer which handles a lot of cases with listener, however if you are using this in your service, use Messenger and Handler. – Roadblock Jul 07 '16 at 09:26
0

Maintain one variable in your music track model class which indicates whether this song is in playing mode or not.

Check that value in your getView() and do coding according to it.

if(model.is_playing)
   //change your code for playing song
else
   //rest songs which are not playing

Now whenever you are changing songs manualy or automaticaly, change that is_playing value, unset it from previous track and set it to currently playing track.

Ravi
  • 34,851
  • 21
  • 122
  • 183
  • Yes, but the BaseAdapter has to be refreshed, doesn't it? How to do it? – PatriceG Jun 30 '16 at 15:07
  • You can use `notifyDatasetChanged()` after updating your list – Ravi Jun 30 '16 at 15:10
  • My problem is that the BaseAdapter is not know in the MusicPlayer class, where the songs change. – PatriceG Jun 30 '16 at 15:12
  • Then you need to create interface which will call a method of track change and use it with your activity or fragment – Ravi Jun 30 '16 at 15:13
  • Could you please give details on how to create the interface? Thanks – PatriceG Jul 01 '16 at 14:59
  • 1
    [How to create our own Listener interface in android?](http://stackoverflow.com/questions/994840/how-to-create-our-own-listener-interface-in-android) – Ravi Jul 05 '16 at 04:16
0

You don't need to implement your own callback interface. Mediaplayer has already an oncompletionlistener when the playing sound is terminated. So you just need to refresh your adapter in the oncompletion method

public class MainActivity extends Activity implements MediaPlayerControl, MediaPlayer.OnCompletionListener{
    private MediaPlayer musicSrv;
    private BaseAdapter songAdt;


    @Override
    public void onCreate() {
        musicSrv.setOnCompletionListener(this);
    }

    @Override
    public void onCompletion(MediaPlayer musicPlayer) {
        songAdt.notifyDataSetChanged();
    }
}
Steve C
  • 1,034
  • 10
  • 12