5

I needed to connect my SeekBar with my MediaPlayer in my App.

I set up the SeekBar via xml like this:

<SeekBar
        android:id="@+id/song_seekbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"/>

and followed this SO answer to implement it.

This is my code:

public class Song_main extends AppCompatActivity {
private final int SONG_REQUEST_CODE = 1;

private Uri song;
private TextView selectSong;
private SeekBar seekBar;
private Handler handler;
private MediaPlayer mediaPlayer;


private boolean repeatPressedTwice = false;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.app_bar_song_main);

    Toolbar toolbar = (Toolbar) findViewById(R.id.song_main_toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayShowTitleEnabled(false);

    seekBar = (SeekBar) findViewById(R.id.song_seekbar);
    handler = new Handler();

    notSelected();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.song, menu);

    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();

    if (id == R.id.song_plus) {
        Intent selectIntent = new Intent(Intent.ACTION_GET_CONTENT);
        selectIntent.setType("audio/*");
        startActivityForResult(selectIntent, SONG_REQUEST_CODE);
    }

    return true;
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == SONG_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
        if ((data != null) && (data.getData()!=null)) {
            song = data.getData();
            setup();
        }
    }
}

private void notSelected() {
    selectSong = (TextView) findViewById(R.id.select_song_textview);
    selectSong.setText(getResources().getString(R.string.song_not_selected));
}

public void onPlayButtonClicked(View v) {
    ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
    if (!mediaPlayer.isPlaying()) {
        mediaPlayer.start();
        pb.setImageResource(R.drawable.pause);

        updateSeekBar();

    } else {
        mediaPlayer.pause();
        pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
    }
}

public void onControlsClicked(View v) {
    if (v.getId() == R.id.fast_forward) {
        int pos = mediaPlayer.getCurrentPosition();
        pos += 1500;
        mediaPlayer.seekTo(pos);
    }
    else if (v.getId() == R.id.fast_backward) {
        int pos = mediaPlayer.getCurrentPosition();
        pos -= 1500;
        mediaPlayer.seekTo(pos);
    }
    else if (v.getId() == R.id.skip_backward) {
        mediaPlayer.seekTo(0);
    }
}

public void onRepeatClicked(View v) {
    if (!repeatPressedTwice) {
        // TODO: change visual color of repeat button
        mediaPlayer.setLooping(true);
        Toast.makeText(this, "repeat enabled", Toast.LENGTH_SHORT).show();
        repeatPressedTwice = true;
    } else {
        mediaPlayer.setLooping(false);
    }

}

private void setup() {
    /* the song has been select setup the interface */


    /* displays song name in title */
    TextView titleView = (TextView) findViewById(R.id.song_appbar_title);
    String songName;

    ContentResolver contentResolver = this.getContentResolver();
    Cursor cursor = contentResolver.query(song, null, null, null, null);

    if (cursor != null && cursor.moveToFirst()) {
        songName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        titleView.setText(songName);
    }


    /* removes the notSelected String */
    selectSong.setVisibility(View.GONE);

    /* setup media player */
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    try {
        mediaPlayer.setDataSource(getApplicationContext(), song);
        mediaPlayer.prepareAsync();
    } catch (IOException e) {
        Toast.makeText(this, "file not found", Toast.LENGTH_SHORT).show();
    }

    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
            /* show media player layout */
            RelativeLayout mpl = (RelativeLayout) findViewById(R.id.media_player_layout);
            mpl.setVisibility(View.VISIBLE);

            mediaPlayer.start();
            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.pause);

        }
    });

    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
        }
    });



    seekBar = (SeekBar) findViewById(R.id.song_seekbar);
    seekBar.setMax(mediaPlayer.getDuration()); 

    updateSeekBar();


}

private void updateSeekBar() {
    seekBar.setProgress(mediaPlayer.getCurrentPosition()/1000);
    handler.postDelayed(runnable, 1000);
}

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        updateSeekBar();
    }
};


@Override
public void onStop() {
    super.onStop();
    if (mediaPlayer!=null)
        mediaPlayer.stop();
}
}

The process starts from the onOptionsItemSelected method.

The seekBar behaves correctly, it increments every second. The problem now is that it finishes way before the song finishes.

I tried adding

seekBar.setMax(mediaPlayer.getDuration());

in the setup method, but that causes the bar not to move at all.

Community
  • 1
  • 1
Daniele
  • 4,163
  • 7
  • 44
  • 95

3 Answers3

5

You need to define separate Runnable and trigger it every x miliseconds (depends on you) once MediaPlayer starts.

Define a function updateSeekbar like,

private void updateSeekBar() {
    audioSeek.setProgress(player.getCurrentPosition());
    txtCurrentTime.setText(milliSecondsToTimer(player.getCurrentPosition()));
    seekHandler.postDelayed(runnable, 50);
}

And Runnable

Runnable runnable = new Runnable() {
    @Override
    public void run() {
        updateSeekBar();
    }
};

Now you just have to call updateSeekbar once when playing starts. In your case:

public void onPlayButtonClicked(View v) {
    ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
    if (!mediaPlayer.isPlaying()) {
        mediaPlayer.start();
        pb.setImageResource(R.drawable.pause);

        updateSeekBar();

    } else {
        mediaPlayer.pause();
        pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
    }
}

FYI

Function milliSecondsToTimer works as follows

private String milliSecondsToTimer(long milliseconds) {
    String finalTimerString = "";
    String secondsString = "";

    // Convert total duration into time
    int hours = (int) (milliseconds / (1000 * 60 * 60));
    int minutes = (int) (milliseconds % (1000 * 60 * 60)) / (1000 * 60);
    int seconds = (int) ((milliseconds % (1000 * 60 * 60)) % (1000 * 60) / 1000);
    // Add hours if there
    if (hours > 0) {
        finalTimerString = hours + ":";
    }

    // Prepending 0 to seconds if it is one digit
    if (seconds < 10) {
        secondsString = "0" + seconds;
    } else {
        secondsString = "" + seconds;
    }

    finalTimerString = finalTimerString + minutes + ":" + secondsString;

    // return timer string
    return finalTimerString;
}

UPDATE

You have called setMax at the wrong place. Update setup() function as follows

private void setup() {
    /* the song has been select setup the interface */
    /* displays song name in title */
    TextView titleView = (TextView) findViewById(R.id.song_appbar_title);
    String songName;

    ContentResolver contentResolver = this.getContentResolver();
    Cursor cursor = contentResolver.query(song, null, null, null, null);

    if (cursor != null && cursor.moveToFirst()) {
        songName = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
        titleView.setText(songName);
    }


    /* removes the notSelected String */
    selectSong.setVisibility(View.GONE);

    /* setup media player */
    mediaPlayer = new MediaPlayer();
    mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
    try {
        mediaPlayer.setDataSource(getApplicationContext(), song);
        mediaPlayer.prepareAsync();
    } catch (IOException e) {
        Toast.makeText(this, "file not found", Toast.LENGTH_SHORT).show();
    }

    mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
        @Override
        public void onPrepared(MediaPlayer mp) {
        /* show media player layout */
            seekBar.setMax(mediaPlayer.getDuration());

            RelativeLayout mpl = (RelativeLayout) findViewById(R.id.media_player_layout);
            mpl.setVisibility(View.VISIBLE);
            mediaPlayer.start();

            updateSeekBar();


            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.pause);

        }
    });

    mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
        @Override
        public void onCompletion(MediaPlayer mp) {
            ImageButton pb = (ImageButton) findViewById(R.id.song_play_button);
            pb.setImageResource(R.drawable.ic_play_arrow_white_24dp);
        }
    });

}
Waqas Ahmed Ansari
  • 1,683
  • 15
  • 30
  • Thanks for the answer. It Kinda works but it does not keep track of the length of the song, so the seekbar progress is finished way before the song actually finishes. How could I fix that? I tried adding `seekBar.setMax(mediaPlayer.getDuration())` but after that the seekbar didn't even seem to move – Daniele Apr 28 '17 at 09:50
  • You have to set it at the time when you are giving data source to `MediaPlayer` inside your `setup` function after `mediaPlayer.setDataSource(...)` – Waqas Ahmed Ansari Apr 28 '17 at 10:09
  • Are you sure? the mediaPlayer isn't ready there yet, because `mediaPlayer.prepareAsync()` isn't even started yet, wouldn't `seekBar.setMax(mediaPlayer.getDuration())` be set to null in that case?. Shouldn't I put it inside the 'setOnPreparedListener()' ? ... As I suspected... it caused the app to crash – Daniele Apr 28 '17 at 11:23
  • Oh sorry, it's my mistake. You should call it after player is prepared – Waqas Ahmed Ansari Apr 28 '17 at 11:34
  • Yes, it causes the seekBar not to move at all. Please have a lock at the edited question. I have put in your code, and explained what is still not working. – Daniele Apr 28 '17 at 11:35
  • I've updated my answer, modify your `setup` function – Waqas Ahmed Ansari Apr 28 '17 at 11:57
  • Tried it out. The seekBar does not move at all. Is the `getDuration()` returned in milliseconds? should I divide it by 1000? – Daniele Apr 28 '17 at 12:15
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/142912/discussion-between-waqas-ahmed-ansari-and-daniele). – Waqas Ahmed Ansari Apr 28 '17 at 12:28
3

You have implement OnSeekBarChangeListener and in onCreate() add the below line:-

seekBar = (SeekBar) findViewById(R.id.seekBar);

And override the onProgressChanged() method , in this method you can set the progress in the seekbar using the below line:

mPlayer.seekTo(progress); 
seekBar.setProgress(progress);

or

After you initialise your MediaPlayer and for example press the play button, you should create an handler and post runnable so you can update your SeekBar (in the UI thread itself) with the current position of your MediaPlayer like this :

private Handler mHandler = new Handler();
//Make sure you update Seekbar on UI thread
MainActivity.this.runOnUiThread(new Runnable(){
    @Override
    public void run() {
        if(mMediaPlayer != null){
            int mCurrentPosition = mMediaPlayer.getCurrentPosition() / 1000;
            mSeekBar.setProgress(mCurrentPosition);
        }
    mHandler.postDelayed(this, 1000);
    }
});

and update that value every second.

If you need to update the MediaPlayer's position while user drag your SeekBar you should add OnSeekBarChangeListener to your SeekBar and do it there :

mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
     @Override
     public void onStopTrackingTouch(SeekBar seekBar) {

     }
     @Override
     public void onStartTrackingTouch(SeekBar seekBar) {

     }
     @Override
     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
         if(mMediaPlayer != null && fromUser){
             mMediaPlayer.seekTo(progress * 1000);
     }
  }
});
Daniele
  • 4,163
  • 7
  • 44
  • 95
2

you need to update your Seek bar when you play a song

public void updateProgressBar() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mHandler.postDelayed(mUpdateTimeTask, 100);
            }
        });

    }  

Below Runnable method to update seekbar

private Runnable mUpdateTimeTask = new Runnable() {
        public void run() {
            if (MusicService.isRunning()) {
                duration = MusicService.getDur();
                long currSongPosition = MusicService.getPosn();
                totTime.setText(Utility.milliSecondsToTimer(duration));
                fromTime.setText(Utility.milliSecondsToTimer(currSongPosition));
                int progress = Utility.getProgressPercentage(currSongPosition, duration);
            songProgressBar.setProgress(progress);
            updateProgressBar();
        }
    }
};

Using this below function you can get progress percentage from song current position and song duration

 public static int getProgressPercentage(long currentDuration, long totalDuration) {
        Double percentage;
        long currentSeconds = (int) (currentDuration / 1000);
        long totalSeconds = (int) (totalDuration / 1000);
        percentage = (((double) currentSeconds) / totalSeconds) * 100;
        return percentage.intValue();
    }
Lavanya Velusamy
  • 333
  • 1
  • 6
  • 16