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?
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