In this tutorial, we will go step-by-step in implementing an easy voice player in Android and avoid all those android’s MediaPlayer unwanted errors. So this could be a good option to be out there in your chat app.

Screenshot

XML

  1. We will create activity_main.xml layout:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
   <?xml version="1.0" encoding="utf-8"?>
   <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
       android:background="@drawable/main_layout_bg"
       android:gravity="center_horizontal"
       android:orientation="horizontal"
       tools:context=".MainActivity"
       android:layout_height="wrap_content"
       android:layout_width="wrap_content"
       android:id="@+id/collectorLinearLayout">
           <LinearLayout
               android:id="@+id/paddedLinearLayout"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
               android:orientation="horizontal"
               android:padding="5dp">
               <ImageView
                   android:clickable="true"
                   android:id="@+id/imgPlay"
                   android:layout_width="45dp"
                   android:layout_height="45dp"
                   android:background="@drawable/play_pause_bg"
                   android:src="@drawable/ic_play_arrow_white_24dp"
                   android:focusable="true" />
               <ImageView
                   android:clickable="true"
                   android:id="@+id/imgPause"
                   android:layout_width="45dp"
                   android:layout_height="45dp"
                   android:background="@drawable/play_pause_bg"
                   android:src="@drawable/ic_pause_white_24dp"
                   android:visibility="gone"
                   android:focusable="true" />
               <LinearLayout
                   android:id="@+id/containerLinearLayout"
                   android:layout_width="wrap_content"
                   android:layout_height="match_parent"
                   android:orientation="vertical">
                   <TextView
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:id="@+id/txtTime"
                       android:layout_gravity="end"
                       android:layout_marginEnd="16dp"
                       android:textSize="10sp"
                       android:text="00:00:00 / 00:00:00"
                       android:layout_marginRight="16dp" />
                   <SeekBar
                       android:id="@+id/seekBar"
                       android:layout_width="225dp"
                       android:layout_marginStart="0dp"
                       android:layout_marginEnd="4dp"
                       android:layout_height="wrap_content"
                       android:layout_marginRight="4dp"
                       android:layout_marginLeft="0dp" />
               </LinearLayout>
           </LinearLayout>
   
   
   </LinearLayout>
  1. Create main_layout_bg.xml in your drawable folder:
1
2
3
4
5
6
7
   <?xml version="1.0" encoding="utf-8"?>
   <shape xmlns:android="http://schemas.android.com/apk/res/android">
       <solid
           android:color="@color/white"/>
       <corners
           android:radius="50dp" />
   </shape>
  1. Create play_pause_bg.xml in your drawable folder:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
   <?xml version="1.0" encoding="utf-8"?>
   <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_pressed="true" >
       <shape android:shape="oval">
   
           <gradient
               android:startColor="@color/pink"
               android:endColor="@color/lightPink" />
           <padding
               android:left="10dp"
               android:top="10dp"
               android:right="10dp"
               android:bottom="10dp" />
       </shape>
   </item>
   <item>
       <shape android:shape="oval">
           <solid
               android:color="@color/pink" />
           <padding
               android:left="10dp"
               android:top="10dp"
               android:right="10dp"
               android:bottom="10dp" />
       </shape>
   </item>
   </selector>
  1. You can customize your colors colors.xml as any other item in this tutorial:
1
2
3
4
5
6
7
8
9
   <?xml version="1.0" encoding="utf-8"?>
   <resources>
       <color name="colorPrimary">#008577</color>
       <color name="colorPrimaryDark">#00574B</color>
       <color name="colorAccent">#D81B60</color>
       <color name="white">#ffffff</color>
       <color name="pink">#fe485a</color>
       <color name="lightPink">#ef818c</color>
   </resources>
  • Use Vector Asset to create your play and pause icons.

JAVA

  1. Now we create our VoicePlayer.java class which will deal with all playing and pausing processes:
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
   package me.jagar.tutorial;
   
   import android.app.Activity;
   import android.content.Context;
   import android.media.AudioManager;
   import android.media.MediaPlayer;
   import android.os.Handler;
   import android.view.View;
   import android.widget.ImageView;
   import android.widget.SeekBar;
   import android.widget.TextView;
   
   import java.io.IOException;
   
   public class VoicePlayer {
   
       private static VoicePlayer mInstance;
       private Context context;
       private ImageView imgPlay, imgPause;
       private SeekBar seekBar;
       private TextView txtProcess;
       private MediaPlayer mediaPlayer;
   
       public VoicePlayer(Context context) {
           this.context = context;
       }
   
       public static synchronized VoicePlayer getInstance(Context context) {
           if (mInstance == null) {
               mInstance = new VoicePlayer(context);
           }
           return mInstance;
       }
       public void init(String path, final ImageView imgPlay, final ImageView imgPause, final SeekBar seekBar, final TextView txtProcess){
   
           this.imgPlay = imgPlay;
           this.imgPause = imgPause;
           this.seekBar = seekBar;
           this.txtProcess = txtProcess;
   
   
           mediaPlayer = new MediaPlayer();
           if (path != null) {
               try {
                   mediaPlayer.setDataSource(path);
                   mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
                   mediaPlayer.prepare();
                   mediaPlayer.setVolume(10, 10);
                   //START and PAUSE are in other listeners
                   mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                       @Override
                       public void onPrepared(MediaPlayer mp) {
                           seekBar.setMax(mp.getDuration());
                           txtProcess.setText("00:00:00/"+convertTime(mp.getDuration() / 1000));
                       }
                   });
                   mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                       @Override
                       public void onCompletion(MediaPlayer mp) {
                           imgPause.setVisibility(View.GONE);
                           imgPlay.setVisibility(View.VISIBLE);
                       }
                   });
   
               } catch (IOException e) {
                   e.printStackTrace();
               }
           }
   
   
           this.seekBar.setOnSeekBarChangeListener(seekBarListener);
           this.imgPlay.setOnClickListener(imgPlayClickListener);
           this.imgPause.setOnClickListener(imgPauseClickListener);
       }
   
   
       //Convert long milli seconds to a formatted String to display it
   
       private static String convertTime(long seconds) {
           long s = seconds % 60;
           long m = (seconds / 60) % 60;
           long h = (seconds / (60 * 60)) % 24;
           return String.format("%02d:%02d:%02d", h,m,s);
       }
   
       //These both functions to avoid mediaplayer errors
   
       public void onStop(){
           try{
               mediaPlayer.stop();
               mediaPlayer.reset();
               mediaPlayer.release();
           }catch (Exception e){
               e.printStackTrace();
           }
       }
   
       public void onPause(){
           try{
               if (mediaPlayer != null){
                   if (mediaPlayer.isPlaying())
                       mediaPlayer.pause();
               }
           }catch (Exception e){
               e.printStackTrace();
           }
           imgPause.setVisibility(View.GONE);
           imgPlay.setVisibility(View.VISIBLE);
       }
   
   
   
       //Components' listeners
   
       View.OnClickListener imgPlayClickListener = new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               imgPause.setVisibility(View.VISIBLE);
               imgPlay.setVisibility(View.GONE);
               mediaPlayer.start();
               try{
                   update(mediaPlayer, txtProcess, seekBar, context);
               }catch (Exception e){
                   e.printStackTrace();
               }
   
           }
       };
   
   
   
       private SeekBar.OnSeekBarChangeListener seekBarListener = new SeekBar.OnSeekBarChangeListener() {
           @Override
           public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
               if (fromUser)
               {
                   mediaPlayer.seekTo(progress);
               }
           }
   
           @Override
           public void onStartTrackingTouch(SeekBar seekBar) {
               imgPause.setVisibility(View.GONE);
               imgPlay.setVisibility(View.VISIBLE);
           }
   
           @Override
           public void onStopTrackingTouch(SeekBar seekBar) {
               imgPlay.setVisibility(View.GONE);
               imgPause.setVisibility(View.VISIBLE);
               mediaPlayer.start();
   
           }
       };
   
       View.OnClickListener imgPauseClickListener = new View.OnClickListener() {
           @Override
           public void onClick(View v) {
               imgPause.setVisibility(View.GONE);
               imgPlay.setVisibility(View.VISIBLE);
               mediaPlayer.pause();
           }
       };
   
   
   
       //Updating seekBar in realtime
       private void update(final MediaPlayer mediaPlayer, final TextView time, final SeekBar seekBar, final Context context) {
           ((Activity)context).runOnUiThread(new Runnable() {
               @Override
               public void run() {
                   seekBar.setProgress(mediaPlayer.getCurrentPosition());
                   if (mediaPlayer.getDuration() - mediaPlayer.getCurrentPosition() > 100) {
                       time.setText(convertTime(mediaPlayer.getCurrentPosition() / 1000) + " / " + convertTime(mediaPlayer.getDuration() / 1000));
                   }
                   else {
                       time.setText(convertTime(mediaPlayer.getDuration() / 1000));
                       seekBar.setProgress(0);
                   }
                   Handler handler = new Handler();
                   try{
                       Runnable runnable = new Runnable() {
                           @Override
                           public void run() {
                               try{
                                   if (mediaPlayer.getCurrentPosition() > -1) {
                                       try {
                                           update(mediaPlayer, time, seekBar, context);
                                       } catch (Exception e) {
                                           e.printStackTrace();
                                       }
                                   }
                               }catch (Exception e){
                                   e.printStackTrace();
                               }
                           }
                       };
                       handler.postDelayed(runnable, 2);
                   }catch (Exception e){
                       e.printStackTrace();
                   }
   
               }
           });
       }
   
   }
  1. We need to read from the storage so add this permission to your manifest file:
1
   <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  1. In our main Activity, we will only ask for permissions and initialize our voice player:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
   package me.jagar.tutorial;
   
   import android.Manifest;
   import android.content.pm.PackageManager;
   import android.os.Environment;
   import android.support.v4.app.ActivityCompat;
   import android.support.v4.content.ContextCompat;
   import android.support.v7.app.AppCompatActivity;
   import android.os.Bundle;
   import android.widget.ImageView;
   import android.widget.SeekBar;
   import android.widget.TextView;
   
   import java.io.File;
   
   public class MainActivity extends AppCompatActivity {
   
       private ImageView imgPlay, imgPause;
       private SeekBar seekBar;
       private TextView txtProcess;
       private String path = Environment.getExternalStorageDirectory().getAbsolutePath()
               + File.separator + "song.mp3";
   
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
   
           imgPlay = findViewById(R.id.imgPlay);
           imgPause = findViewById(R.id.imgPause);
           seekBar = findViewById(R.id.seekBar);
           txtProcess = findViewById(R.id.txtTime);
   
   
           if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
                   != PackageManager.PERMISSION_GRANTED) {
               // Permission is not granted
               ActivityCompat.requestPermissions(MainActivity.this,
                       new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                       101);
           }else{
               VoicePlayer.getInstance(MainActivity.this).init(path, imgPlay, imgPause, seekBar, txtProcess);
           }
   
       }
   
       @Override
       public void onRequestPermissionsResult(int requestCode,
                                              String[] permissions, int[] grantResults) {
           switch (requestCode) {
               case 101: {
                   if (grantResults.length > 0
                           && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
   
                       VoicePlayer.getInstance(MainActivity.this).init(path, imgPlay, imgPause, seekBar, txtProcess);
   
                   } else {
   
                   }
                   return;
               }
   
           }
       }
   
       @Override
       protected void onStop() {
           super.onStop();
           VoicePlayer.getInstance(MainActivity.this).onStop();
       }
   
       @Override
       protected void onPause() {
           super.onPause();
           VoicePlayer.getInstance(MainActivity.this).onPause();
       }
   }

Recommendations

  • You can get the same result and customization with my library ChatVoicePlayer.
  • Use Dexter library for permissions.