Sunday, May 4, 2014

Re-set On..Listener after mediaPlayer.release and re-create MediaPlayer

It's a BIG bug in last exercise Implement OnErrorListener for MP3 Player using MediaPlayer: after we release the old mediaPlayer, and create again, the original OnPreparedListener(), OnCompletionListener() and OnErrorListener() are not associate with the new mediaPlayer; and will not be called. So we should move all code of setOnPreparedListener(), setOnCompletionListener() and setOnErrorListener() to initMediaPlayer(); to update the Listeners after new mediaPlayer created.

MainActivity.java
package com.example.androidmp3player;

import android.support.v7.app.ActionBarActivity;
import android.support.v4.app.Fragment;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

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

  if (savedInstanceState == null) {
   getSupportFragmentManager().beginTransaction()
     .add(R.id.container, new PlaceholderFragment()).commit();
  }
 }

 @Override
 public boolean onCreateOptionsMenu(Menu menu) {

  // Inflate the menu; this adds items to the action bar if it is present.
  getMenuInflater().inflate(R.menu.main, menu);
  return true;
 }

 @Override
 public boolean onOptionsItemSelected(MenuItem item) {
  // Handle action bar item clicks here. The action bar will
  // automatically handle clicks on the Home/Up button, so long
  // as you specify a parent activity in AndroidManifest.xml.
  int id = item.getItemId();
  if (id == R.id.action_settings) {
   return true;
  }
  return super.onOptionsItemSelected(item);
 }

 /**
  * A placeholder fragment containing a simple view.
  */
 public static class PlaceholderFragment extends Fragment {

  Button btnStart, btnPause, btnStop, btnSeek;
  TextView textState, textDuration, textPosition;

  MediaPlayer mediaPlayer;

  private int stateMediaPlayer;
  private final int STATE_Idle = 0;
  private final int STATE_Initialized = 1;
  private final int STATE_Preparing = 2;
  private final int STATE_Prepared = 3;
  private final int STATE_Started = 4;
  private final int STATE_Paused = 5;
  private final int STATE_Stopped = 6;
  private final int STATE_PlaybackCompleted = 7;
  private final int STATE_End = 8;
  private final int STATE_Error = 9;
  
  public PlaceholderFragment() {
  }

  @Override
  public void onCreate(Bundle savedInstanceState) {
   // TODO Auto-generated method stub
   super.onCreate(savedInstanceState);
   initMediaPlayer();
  }

  @Override
  public void onDestroy() {

   Toast.makeText(getActivity(), 
    "release mediaPlayer", 
    Toast.LENGTH_LONG).show();
   mediaPlayer.release();
   mediaPlayer = null;
   setPlayerState(STATE_End);
   
   super.onDestroy();
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {
   View rootView = inflater.inflate(R.layout.fragment_main, container,
     false);
   btnStart = (Button) rootView.findViewById(R.id.start);
   btnPause = (Button) rootView.findViewById(R.id.pause);
   btnStop = (Button) rootView.findViewById(R.id.stop);
   btnSeek = (Button) rootView.findViewById(R.id.seekto);
   
   btnStart.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick(View v) {
     
     if(stateMediaPlayer==STATE_Prepared 
      || stateMediaPlayer==STATE_Started
      || stateMediaPlayer==STATE_Paused
      || stateMediaPlayer==STATE_PlaybackCompleted){
      mediaPlayer.start();
      setPlayerState(STATE_Started);
      
      displayDurationPosition();
     }else{
      Toast.makeText(getActivity(), 
       "Play at Invalid state!", 
       Toast.LENGTH_LONG).show();
     }
    }
   });
   
   btnPause.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick(View v) {
     
     if(stateMediaPlayer==STATE_Started
      || stateMediaPlayer==STATE_Paused
      || stateMediaPlayer==STATE_Prepared //simulate a error case
      || stateMediaPlayer==STATE_PlaybackCompleted){
      mediaPlayer.pause();
      setPlayerState(STATE_Paused);
      
      displayDurationPosition();
     }else{
      Toast.makeText(getActivity(), 
       "Pause at Invalid state!", 
       Toast.LENGTH_LONG).show();
     }
    }
   });
   
   btnStop.setOnClickListener(new OnClickListener() {
    
    @Override
    public void onClick(View v) {
     
     if(stateMediaPlayer==STATE_Prepared
      || stateMediaPlayer==STATE_Started
      || stateMediaPlayer==STATE_Stopped
      || stateMediaPlayer==STATE_Paused
      || stateMediaPlayer==STATE_PlaybackCompleted){
      
      //Stop
      mediaPlayer.stop();
      setPlayerState(STATE_Stopped);
      
      //then parepare in background thread
      mediaPlayer.prepareAsync();
      setPlayerState(STATE_Preparing);
      
      displayDurationPosition();
     }else{
      Toast.makeText(getActivity(), 
       "Stop at Invalid state!", 
       Toast.LENGTH_LONG).show();
     }
     
    }
   });
   
   btnSeek.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View v) {
     if(stateMediaPlayer==STATE_Prepared
      || stateMediaPlayer==STATE_Started
      || stateMediaPlayer==STATE_Paused
      || stateMediaPlayer==STATE_PlaybackCompleted){
      mediaPlayer.seekTo(0);
      
      displayDurationPosition();
     }else{
      Toast.makeText(getActivity(), 
       "SeekTo at Invalid state!", 
       Toast.LENGTH_LONG).show();
     }
    }});

   textState = (TextView) rootView.findViewById(R.id.state);
   textState.setText(getPlayerState());
   
   textDuration = (TextView) rootView.findViewById(R.id.duration);
   textPosition = (TextView) rootView.findViewById(R.id.position);
   displayDurationPosition();
   
   textDuration.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View v) {
     displayDurationPosition();
    }});
   
   textPosition.setOnClickListener(new OnClickListener(){

    @Override
    public void onClick(View v) {
     displayDurationPosition();
    }});
   
   return rootView;
  }
  
  private void displayDurationPosition(){
   textDuration.setText(
     "Duration: " + mediaPlayer.getDuration() + " ms");
   textPosition.setText(
     "Current Position: " + mediaPlayer.getCurrentPosition() + " ms");
  }

  private void initMediaPlayer() {
   Toast.makeText(getActivity(), 
     "initMediaPlayer()", 
     Toast.LENGTH_LONG).show();
   mediaPlayer = MediaPlayer.create(getActivity(), R.raw.vespers);
   setPlayerState(STATE_Prepared);
   
   mediaPlayer.setOnPreparedListener(new OnPreparedListener(){

    @Override
    public void onPrepared(MediaPlayer mp) {
     setPlayerState(STATE_Prepared);
     displayDurationPosition();
    }});
   
   mediaPlayer.setOnCompletionListener(new OnCompletionListener(){

    @Override
    public void onCompletion(MediaPlayer mp) {
     setPlayerState(STATE_PlaybackCompleted);
     displayDurationPosition();
    }});
   
   //Handle Error
   mediaPlayer.setOnErrorListener(new OnErrorListener(){

    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
     
     setPlayerState(STATE_Error);
     
     String errorWhat;
     switch(what){
     case MediaPlayer.MEDIA_ERROR_UNKNOWN:
      errorWhat = "MEDIA_ERROR_UNKNOWN";
      break;
     case MediaPlayer.MEDIA_ERROR_SERVER_DIED:
      errorWhat = "MEDIA_ERROR_SERVER_DIED";
      break;
     default:
      errorWhat = "!";
     }
     
     String errorExtra;
     switch(extra){
     case MediaPlayer.MEDIA_ERROR_IO:
      errorExtra = "MEDIA_ERROR_IO";
      break;
     case MediaPlayer.MEDIA_ERROR_MALFORMED:
      errorExtra = "MEDIA_ERROR_MALFORMED";
      break;
     case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:
      errorExtra = "MEDIA_ERROR_UNSUPPORTED";
      break;
     case MediaPlayer.MEDIA_ERROR_TIMED_OUT:
      errorExtra = "MEDIA_ERROR_TIMED_OUT";
      break;
     default:
      errorExtra = "!";
     }

     Toast.makeText(getActivity(), 
      "Error" + "\n"
      + errorWhat + "\n"
      + errorExtra,
      Toast.LENGTH_LONG).show();
     
     //release 
     mp.release();
     initMediaPlayer();
     
     return true;
    }});
  }
  
  private void setPlayerState(int st){
   stateMediaPlayer = st;
   
   String stringState = getPlayerState();
   if(textState!=null){
    textState.setText(stringState);
   }else{
    Toast.makeText(getActivity(), 
      stringState, Toast.LENGTH_LONG).show();
   }
   
  }
  
  private String getPlayerState(){
   String strSt;
   switch(stateMediaPlayer){
   case STATE_Idle:
    strSt = "Idle";
    break;
   case STATE_Initialized:
    strSt = "Initialized";
    break;
   case STATE_Preparing:
    strSt = "Preparing";
    break;
   case STATE_Prepared:
    strSt = "Prepared";
    break;
   case STATE_Started:
    strSt = "Started";
    break;
   case STATE_Paused:
    strSt = "Paused";
    break;
   case STATE_Stopped:
    strSt = "Stopped";
    break;
   case STATE_PlaybackCompleted:
    strSt = "PlaybackCompleted";
    break;
   case STATE_End:
    strSt = "End";
    break;
   case STATE_Error:
    strSt = "Error";
    break;
   default:
    strSt = "unknown...";
   }
   return strSt;
  }
 }

}


download filesDownload the files, without mp3.

Next:
Implement time bar for MediaPlayer, also introduce bug of MediaPlayer.seekTo()

No comments: