android-studioandroid-serviceandroid-mediaplayerandroid-service-bindingmediacontroller

Music Play Next and Play Previous are not working in Android studio


I had 2 Activities (namely MainActivity which shows list of songs and a default media controller at the bottom, and a AndroidBuildingMusicPlayerActivity with a custom made layout for playback) and a Bound Service. The issue is playNext and playPrevios (from MusicService class) features worked fine from the MainActivity controller while works wrong (kind of shuffled) from the other activity. I later removed the MainActivity controller (since I don't want it). I worked on correcting the AndroidBuildingMusicPlayerActivity's playNext and playPrevios but still the issue remains. How do I solve this? Thanks in advance.

These are the codes:

MusicService:

public void onCreate(){
    super.onCreate();
    songPosn=0;
    player = new MediaPlayer();
    initMusicPlayer();
    rand=new Random();
}

@Override
public void onDestroy() {
    stopForeground(true);
}


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

//initializes the MediaPlayer class
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;
}

//Let's now set the app up to play a track
public void playSong(){
    player.reset();
    //get song
    Song playSong = songs.get(songPosn);
    songTitle=playSong.getTitle();
    //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();
}

//We will call this when the user picks a song from the list.
public void setSong(int songIndex){
    songPosn=songIndex;
}

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();
}


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 setShuffle(){
    if(shuffle) shuffle=false;
    else shuffle=true;
}


//When the MediaPlayer is prepared, the onPrepared method will be executed.
@Override
public void onPrepared(MediaPlayer mp) {
    //start playback
    mp.start();
    Intent notIntent = new Intent(this, MainActivity.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.play)
            .setTicker(songTitle)
            .setOngoing(true)
            .setContentTitle("Playing").setContentText(songTitle);
    Notification not = builder.build();

    startForeground(NOTIFY_ID, not);
}

@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;
}


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

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

AndroidBuildingMusicPlayerActivity:

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

// All player buttons
    btnPlay = (ImageButton) findViewById(R.id.btnPlay);
    btnNext = (ImageButton) findViewById(R.id.btnNext);
    btnPrevious = (ImageButton) findViewById(R.id.btnPrevious);
    btnRepeat = (ImageButton) findViewById(R.id.btnRepeat);
    btnShuffle = (ImageButton) findViewById(R.id.btnShuffle);
    songProgressBar = (SeekBar) findViewById(R.id.songProgressBar);
    songTitleLabel = (TextView) findViewById(R.id.songTitle);
    songCurrentDurationLabel = (TextView) findViewById(R.id.songCurrentDurationLabel);
    songTotalDurationLabel = (TextView) findViewById(R.id.songTotalDurationLabel);
    equalizer = (EqualizerView) findViewById(R.id.equalizer_view);
    // Mediaplayer
    mp = new MediaPlayer();
    utils = new Utilities();

    songProgressBar.setOnSeekBarChangeListener(this);
    // set Progress bar values
    songProgressBar.setProgress(0);
    songProgressBar.setMax(100);
    // Updating progress bar
    updateProgressBar();
}

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

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

//connect to the service
private ServiceConnection musicConnection2 = new ServiceConnection(){

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        MusicService.MusicBinder binder = (MusicService.MusicBinder)service;
        //get service
        ms = binder.getService();
        musicBound2 = true;
    }

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

public void updateProgressBar() {
    mHandler.postDelayed(mUpdateTimeTask, 100);
}

/**
 * Background Runnable thread
 * */
private Runnable mUpdateTimeTask = new Runnable() {
    public void run() {
        long totalDuration = ms.getDur();
        long currentDuration = ms.getPosn();

        // Displaying Total Duration time
        songTotalDurationLabel.setText(""+utils.milliSecondsToTimer(totalDuration));
        // Displaying time completed playing
        songCurrentDurationLabel.setText(""+utils.milliSecondsToTimer(currentDuration));

        // Updating progress bar
        int progress = (int)(utils.getProgressPercentage(currentDuration, totalDuration));
        //Log.d("Progress", ""+progress);
        songProgressBar.setProgress(progress);

        // Running this thread after 100 milliseconds
        mHandler.postDelayed(this, 100);
    }
};

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {

}

/**
 * When user starts moving the progress handler
 * */
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
    // remove message Handler from updating progress bar
    mHandler.removeCallbacks(mUpdateTimeTask);
}

/**
 * When user stops moving the progress hanlder
 * */
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
    mHandler.removeCallbacks(mUpdateTimeTask);
    int totalDuration = ms.getDur();
    int currentPosition = utils.progressToTimer(seekBar.getProgress(), totalDuration);

    // forward or backward to certain seconds
    ms.seek(currentPosition);

    // update timer progress again
    updateProgressBar();
}


public void playClicked(View view){
    if(!ms.isPng()){
        btnPlay.setImageResource(R.drawable.btn_pause);
        ms.go();
        equalizer.animateBars();
    }
    else {
        btnPlay.setImageResource(R.drawable.btn_play);
        ms.pausePlayer();
        equalizer.stopBars();
    }
}

public void prvClicked(View view){
    ms.playPrev();
}

public void nextClicked(View view){
    ms.playNext();
}

MainActivity:

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

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
    if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {

        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
        // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
        // app-defined int constant
        return;
    }
    }
    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.getTitle().compareTo(b.getTitle());
        }
    });
    SongAdapter songAdt = new SongAdapter(this, songList);
    songView.setAdapter(songAdt);
}


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

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

//connect to the service
private 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;
    }
};

public void getSongList() {
    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);
        //add songs to list
        do {
            long thisId = musicCursor.getLong(idColumn);
            String thisTitle = musicCursor.getString(titleColumn);
            String thisArtist = musicCursor.getString(artistColumn);
            songList.add(new Song(thisId, thisTitle, thisArtist));
        }
        while (musicCursor.moveToNext());
    }
}





//when user clicks on a song
public void songPicked(View view){
    musicSrv.setSong(Integer.parseInt(view.getTag().toString()));
    musicSrv.playSong();
    Intent intent = new Intent(this, AndroidBuildingMusicPlayerActivity.class);
    startActivity(intent);

}

Errors I found:

Attempt to call getDuration without a valid mediaplayer
E/MediaPlayer: error (-38, 0).

So just now I removed the seekbar from AndroidBuildingMusicPlayerActivity which solved the issue, but how do I implement the seek bar then?


Solution

  • Finally I found it

    In my case the issue was with the seekbar. In my Service class I changed:

    @Override
    public void onPrepared(MediaPlayer mp) {
    mp.start();}
    
    public getDur() { return mediaPalyer.getDuration} 
    

    to

    int dr; //at the top inside Service class
    
    @Override
    public void onPrepared(MediaPlayer mp) {
    mp.start();
    dr = player.getDuration();}
    
    public getDur() { return dr}
    

    This helped me: https://stackoverflow.com/a/5711274/6737655