I am having a problem with the following code. I have invoked setPrevNextListener on my MediaController and defined two onClickListeners for both Prev and Next. When I click the next button instead of going forward one track I go forward two tracks. This seems to be due to the fact that onCompletion is being called somehow. Does MediaPlayer.reset() invoke onCompletion? I have included logcat output and my code. Let me know if I'm doing something wrong. Thanks in advance.
Logcat:
02-24 00:36:34.826: D/MP(6675): Next Button Clicked, index was: 0
02-24 00:36:34.837: D/MP(6675): About to call Reset()
02-24 00:36:34.906: D/MP(6675): Inside setUpPlayer
02-24 00:36:34.917: D/MP(6675): Called setDataSource with index: 1
02-24 00:36:34.917: D/MP(6675): Leaving setUpPlayer
02-24 00:36:34.917: D/MP(6675): About to call prepareAsync()
02-24 00:36:34.937: D/MP(6675): Leaving next button
02-24 00:36:35.226: E/MediaPlayer(6675): Attempt to call getDuration without a valid mediaplayer
02-24 00:36:35.226: E/MediaPlayer(6675): error (-38, 0)
02-24 00:36:35.276: E/MediaPlayer(6675): Error (-38,0)
02-24 00:36:35.287: D/MP(6675): Inside onCompletion
02-24 00:36:35.337: D/MP(6675): About to call Reset()
02-24 00:36:35.347: D/MP(6675): Inside setUpPlayer
02-24 00:36:35.356: D/MP(6675): Called setDataSource with index: 2
02-24 00:36:35.356: D/MP(6675): Leaving setUpPlayer
02-24 00:36:35.356: D/MP(6675): About to call prepareAsync()
02-24 00:36:35.377: D/MP(6675): Leaving onCompletion
02-24 00:36:36.517: D/MP(6675): Inside onPrepared, index is: 2
02-24 00:36:36.577: D/MP(6675): Leaving onPrepared
Code:
public class MusicPlayerActivity extends Activity implements OnPreparedListener, OnCompletionListener, MediaController.MediaPlayerControl {
private MediaPlayer mp;
private MediaController mc;
private SonarDatabase db;
private SubsonicAPIConnector sonic;
ArrayList<String> songs;
int index = 0;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.music_player);
// Get params from intent
Intent i = getIntent();
String albumId = i.getStringExtra("albumId");
String artistId = i.getStringExtra("artistId");
String albumName = i.getStringExtra("albumName");
String songName = i.getStringExtra("songName");
String songId = i.getStringExtra("songId");
String coverArt = "al-"+albumId+".jpeg";
String duration = i.getStringExtra("duration");
Log.d("MP", "results: albumName:" + albumName + ", songName: " + songName + ", songId: "
+ songId + ", duration: " + duration + ", coverArt: " + coverArt + ", artistId: "
+ artistId + ", albumId: " + albumId);
db = new SonarDatabase(getApplicationContext());
sonic = new SubsonicAPIConnector(db.getURL(), db.getUsername(), db.getPassword());
String artistName = db.getArtistNameById(artistId);
setTitle("Now Playing");
songs = db.getSongListForAlbum(albumId);
index = songs.indexOf(songId);
Log.d("MP", "songid: " + songId + ", index: " + index);
// Update text views with song information
TextView txtArtist = (TextView) findViewById(R.id.txtArtistName);
txtArtist.setText(artistName);
TextView txtAlbum = (TextView) findViewById(R.id.txtAlbumName);
txtAlbum.setText(albumName);
TextView txtSong = (TextView) findViewById(R.id.txtSongName);
txtSong.setText(songName);
// Show the album art
File img = new File(this.getFilesDir()+"/covers/"+coverArt);
if(img.exists()) {
Log.d("MP", "Found image at: " + img.toString());
ImageView imgView = (ImageView) findViewById(R.id.cover_art);
imgView.setImageBitmap(BitmapFactory.decodeFile(img.getPath()));
} else {
Log.d("MP", "Couldn't find image: " + img.toString());
}
// Create the media player and controller
mp = new MediaPlayer();
mp.setOnPreparedListener(this);
mp.setOnCompletionListener(this);
mc = new NonHidingMediaController(this);
mc.setPrevNextListeners(new View.OnClickListener() {
@Override
public void onClick(View v) {
// next button clicked
Log.d("MP", "Next Button Clicked, index was: " + index);
playNextTrack();
Log.d("MP", "Leaving next button");
}
}, new View.OnClickListener() {
@Override
public void onClick(View v) {
// previous button clicked
Log.d("MP", "Previous button clicked, index was: " + index);
if(mp.isPlaying() && mp.getCurrentPosition() < 2000 && index != 0) {
Log.d("MP", "Start the prior song.");
playPreviousTrack();
} else {
Log.d("MP", "Restart this song.");
restartCurrentTrack();
}
Log.d("MP", "Leaving previous button");
}
});
mc.setMediaPlayer(this);
setUpPlayer();
Log.d("MP", "About to call prepareAsync()");
mp.prepareAsync();
}
@Override
public void onCompletion(MediaPlayer mp) {
Log.d("MP", "Inside onCompletion");
//TODO add code for scrobbling here.
playNextTrack();
Log.d("MP", "Leaving onCompletion");
}
private void playNextTrack() {
index++;
TextView txtSong = (TextView) findViewById(R.id.txtSongName);
txtSong.setText(db.getSongNameById(songs.get(index)));
Log.d("MP", "About to call Reset()");
mp.reset();
setUpPlayer();
Log.d("MP", "About to call prepareAsync()");
mp.prepareAsync();
}
private void playPreviousTrack() {
index--;
TextView txtSong = (TextView) findViewById(R.id.txtSongName);
txtSong.setText(db.getSongNameById(songs.get(index)));
Log.d("MP", "About to call Reset()");
mp.reset();
setUpPlayer();
Log.d("MP", "About to call prepareAsync()");
mp.prepareAsync();
}
private void restartCurrentTrack() {
mp.seekTo(0);
}
public void onPrepared(MediaPlayer mp) {
Log.d("MP", "Inside onPrepared, index is: " + index);
mc.setMediaPlayer(this);
mc.setAnchorView(findViewById(R.id.music_player_view));
mp.start();
handler.post(new Runnable() {
public void run() {
mc.setEnabled(true);
mc.show();
}
});
Log.d("MP", "Leaving onPrepared");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_music_player, menu);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("MP", "OnTouchEvent called");
mc.show();
return false;
}
private void setUpPlayer() {
Log.d("MP", "Inside setUpPlayer");
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
try {
mp.setDataSource(sonic.streamString("&id=" + songs.get(index)));
Log.d("MP", "Called setDataSource with index: " + index);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("MP", "Leaving setUpPlayer");
}
@Override
protected void onStop() {
super.onStop();
mc.hide();
mp.stop();
mp.release();
}
public void start() {
mp.start();
}
public void pause() {
mp.pause();
}
public int getDuration() {
return mp.getDuration();
}
public int getCurrentPosition() {
return mp.getCurrentPosition();
}
public void seekTo(int i) {
mp.seekTo(i);
}
public boolean isPlaying() {
return mp.isPlaying();
}
public int getBufferPercentage() {
return 0;
}
public boolean canPause() {
return true;
}
public boolean canSeekBackward() {
return true;
}
public boolean canSeekForward() {
return true;
}
public class NonHidingMediaController extends MediaController {
public NonHidingMediaController(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonHidingMediaController(Context context, boolean useFastForward) {
super(context, useFastForward);
}
public NonHidingMediaController(Context context) {
super(context);
}
@Override
public void show(int timeout) {
super.show(0);
}
}
}
I did a little digging through the android source and found that any unhandled errors cause MediaPlayer to call the onCompletion handler. In this case you appear to be calling getDuration in either the Idle, Initialized or Error states. Possible from inside the media controller. I'd suggest adding some Log.d() right before any places that call getDuration to see where the call causing the issue is.
Add MediaPlayer.OnErrorLisener and return true from the overrided method
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
return true;
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With