Search code examples
javaandroidxmlmedia-playerseekbar

Android - SeekBar and MediaPlayer


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.


Solution

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