Sunday, May 4, 2014

Implement OnErrorListener for MP3 Player using MediaPlayer

The former post show how to Implement Android MP3 Player using MediaPlayer, without handle error case. To detect error for MediaPlayer, we can implement our OnErrorListener, and add it to MediaPlayer by calling setOnErrorListener().

Please note that this exercise show how to implement and add OnErrorListener, not how to solve the error.


To simpulate error in our exercise, modify the OnClickListener of btnPause, to accept if (stateMediaPlayer==STATE_Prepared), its a invalide case. When user click on the Pause button when the MediaPlayer is in STATE_Prepared, error will occur. Without OnErrorListener, OnCompletionListener will be called, many error message will be dispalyed in LogCat, and the MP3 cannot be play anymore.

Modify MainActivity.java in the former exercise to implement OnErrorListener. When error occur, release the old MediaPlay and instantiate a new one by calling initMediaPlayer().

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

}


Keep using the layout files in last exercise.

download filesDownload the files, without mp3 file.

Remark: It's a BIG bug here, read next post: Re-set On..Listener after mediaPlayer.release and re-create MediaPlayer.

No comments: