Needs
1.recording_item_layout.xml
2.activity_recording_list.xml
3.activity_record.xml
4.Recording.java
5.RecordingAdapter.java
6.
RecordingListActivity.java
7.RecordActivity.java
Step 1:
recording_item_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:paddingTop="10dp"
android:paddingBottom="10dp">
<ImageView
android:id="@+id/imageViewPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/play_audio"
android:layout_gravity="center_vertical"
android:clickable="true"
android:layout_marginLeft="10sp"
android:tint="@android:color/darker_gray"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/textViewRecordingname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="Voice 001"
android:layout_marginLeft="50sp"
android:textColor="@android:color/holo_red_light"
android:textStyle="bold"
/>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:visibility="gone"
/>
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@android:color/darker_gray"
android:layout_marginTop="15dp" />
</LinearLayout>
</RelativeLayout>
Step 2:
activity_recording_list.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
tools:context=".RecordingListActivity">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Recording List"/>
</android.support.v7.widget.Toolbar>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerViewRecordings"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar"
/>
<TextView
android:id="@+id/textViewNoRecordings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Recordings found."
android:visibility="gone"
android:layout_centerInParent="true"
android:textColor="@android:color/darker_gray"
/>
</RelativeLayout>
Step 3:
activity_record.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
tools:context=".Test">
<LinearLayout
android:id="@+id/linearLayoutRecorder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/toolbar"
android:layout_margin="10sp"
android:orientation="vertical">
<TextView
android:id="@+id/text_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|right"
android:padding="20sp"
android:text="Close"
android:textColor="@color/colorPrimary"
android:textSize="17sp" />
<Chronometer
android:id="@+id/chronometerTimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textColor="@color/colorPrimary"
android:textSize="60sp" />
<TextView
android:id="@+id/text_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="10sp"
android:textSize="15sp" />
<LinearLayout
android:id="@+id/linearLayoutPlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:visibility="invisible">
<LinearLayout
android:id="@+id/layout_play_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageViewPlay"
android:layout_width="20sp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:src="@mipmap/play_audio" />
<ImageView
android:id="@+id/imageViewPause"
android:layout_width="20sp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:visibility="gone"
android:src="@mipmap/stop_audio" />
</LinearLayout>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_weight="1" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageViewRecord"
android:layout_width="70sp"
android:layout_height="match_parent"
android:src="@mipmap/microphone" />
<ImageView
android:id="@+id/imageViewStop"
android:layout_width="70sp"
android:layout_height="match_parent"
android:src="@mipmap/muted"
android:visibility="gone" />
</LinearLayout>
<Button
android:id="@+id/button_submit"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_marginLeft="40dp"
android:layout_marginTop="30sp"
android:layout_marginRight="40dp"
android:layout_marginBottom="40sp"
android:background="@drawable/layout_shape1"
android:text="@string/submit"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Small"
android:textColor="@android:color/white" />
</LinearLayout>
</RelativeLayout>
Step 4:
Recording.java
public class Recording {
String Uri, fileName;
boolean isPlaying = false;
public Recording(String uri, String fileName, boolean isPlaying) {
Uri = uri;
this.fileName = fileName;
this.isPlaying = isPlaying;
}
public String getUri() {
return Uri;
}
public String getFileName() {
return fileName;
}
public boolean isPlaying() {
return isPlaying;
}
public void setPlaying(boolean playing){
this.isPlaying = playing;
}
}
Step 5:
RecordingAdapter.java
public class RecordingAdapter extends RecyclerView.Adapter<RecordingAdapter.ViewHolder>{
private ArrayList<Recording> recordingArrayList;
private Context context;
private MediaPlayer mPlayer;
private boolean isPlaying = false;
private int last_index = -1;
public RecordingAdapter(Context context, ArrayList<Recording> recordingArrayList){
this.context = context;
this.recordingArrayList = recordingArrayList;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.recording_item_layout,parent,false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
setUpData(holder,position);
}
@Override
public int getItemCount() {
return recordingArrayList.size();
}
private void setUpData(ViewHolder holder, int position) {
Recording recording = recordingArrayList.get(position);
holder.textViewName.setText(recording.getFileName());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (recording.isPlaying()) {
holder.imageViewPlay.setImageResource(R.mipmap.play_audio);
TransitionManager.beginDelayedTransition((ViewGroup) holder.itemView);
holder.seekBar.setVisibility(View.VISIBLE);
holder.seekUpdation(holder);
} else {
holder.imageViewPlay.setImageResource(R.mipmap.play_audio);
TransitionManager.beginDelayedTransition((ViewGroup) holder.itemView);
holder.seekBar.setVisibility(View.GONE);
}
}
holder.manageSeekBar(holder);
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageViewPlay;
SeekBar seekBar;
TextView textViewName;
private String recordingUri;
private int lastProgress = 0;
private Handler mHandler = new Handler();
ViewHolder holder;
public ViewHolder(View itemView) {
super(itemView);
imageViewPlay = itemView.findViewById(R.id.imageViewPlay);
seekBar = itemView.findViewById(R.id.seekBar);
textViewName = itemView.findViewById(R.id.textViewRecordingname);
imageViewPlay.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
int position = getAdapterPosition();
Recording recording = recordingArrayList.get(position);
recordingUri = recording.getUri();
if( isPlaying ){
stopPlaying();
if( position == last_index ){
recording.setPlaying(false);
stopPlaying();
notifyItemChanged(position);
}else{
markAllPaused();
recording.setPlaying(true);
notifyItemChanged(position);
startPlaying(recording,position);
last_index = position;
}
}else {
startPlaying(recording,position);
recording.setPlaying(true);
seekBar.setMax(mPlayer.getDuration());
Log.d("isPlayin","False");
notifyItemChanged(position);
last_index = position;
}
}
});
}
public void manageSeekBar(ViewHolder holder){
holder.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if( mPlayer!=null && fromUser ){
mPlayer.seekTo(progress);
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
private void markAllPaused() {
for( int i=0; i < recordingArrayList.size(); i++ ){
recordingArrayList.get(i).setPlaying(false);
recordingArrayList.set(i,recordingArrayList.get(i));
}
notifyDataSetChanged();
}
Runnable runnable = new Runnable() {
@Override
public void run() {
seekUpdation(holder);
}
};
private void seekUpdation(ViewHolder holder) {
this.holder = holder;
if(mPlayer != null){
int mCurrentPosition = mPlayer.getCurrentPosition() ;
holder.seekBar.setMax(mPlayer.getDuration());
holder.seekBar.setProgress(mCurrentPosition);
lastProgress = mCurrentPosition;
}
mHandler.postDelayed(runnable, 100);
}
private void stopPlaying() {
try{
mPlayer.release();
}catch (Exception e){
e.printStackTrace();
}
mPlayer = null;
isPlaying = false;
}
private void startPlaying(final Recording audio, final int position) {
mPlayer = new MediaPlayer();
try {
mPlayer.setDataSource(recordingUri);
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
Log.e("LOG_TAG", "prepare() failed");
}
//showing the pause button
seekBar.setMax(mPlayer.getDuration());
isPlaying = true;
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
audio.setPlaying(false);
notifyItemChanged(position);
}
});
}
}
}
Step 6:
RecordingListActivity.java
public class RecordingListActivity extends AppCompatActivity {
private Toolbar toolbar;
private RecyclerView recyclerViewRecordings;
private ArrayList<Recording> recordingArraylist;
private RecordingAdapter recordingAdapter;
private TextView textViewNoRecordings;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recording_list);
initViews();
fetchRecordings();
}
private void initViews() {
recordingArraylist = new ArrayList<Recording>();
/** enabling back button ***/
// getSupportActionBar().setDisplayHomeAsUpEnabled(true);
/** setting up recyclerView **/
recyclerViewRecordings = (RecyclerView) findViewById(R.id.recyclerViewRecordings);
recyclerViewRecordings.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerViewRecordings.setHasFixedSize(true);
textViewNoRecordings = (TextView) findViewById(R.id.textViewNoRecordings);
}
private void fetchRecordings() {
File root = android.os.Environment.getExternalStorageDirectory();
String path = root.getAbsolutePath() + "/VoiceRecorderSimplifiedCoding/Audios";
Log.d("Files", "Path: " + path);
File directory = new File(path);
File[] files = directory.listFiles();
Log.d("Files", "Size: " + files.length);
if (files != null) {
for (int i = 0; i < files.length; i++) {
Log.d("Files", "FileName:" + files[i].getName());
String fileName = files[i].getName();
String recordingUri = root.getAbsolutePath() + "/VoiceRecorderSimplifiedCoding/Audios/" + fileName;
Recording recording = new Recording(recordingUri, fileName, false);
recordingArraylist.add(recording);
}
textViewNoRecordings.setVisibility(View.GONE);
recyclerViewRecordings.setVisibility(View.VISIBLE);
setAdaptertoRecyclerView();
} else {
textViewNoRecordings.setVisibility(View.VISIBLE);
recyclerViewRecordings.setVisibility(View.GONE);
}
}
private void setAdaptertoRecyclerView() {
recordingAdapter = new RecordingAdapter(this, recordingArraylist);
recyclerViewRecordings.setAdapter(recordingAdapter);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
this.finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
Step 7:
RecordActivity.java
public class RecordActivity extends AppCompatActivity implements View.OnClickListener {
private int RECORD_AUDIO_REQUEST_CODE =123 ;
private Toolbar toolbar;
TextView tx_list;
private Chronometer chronometer;
private ImageView imageViewRecord, imageViewPlay, imageViewStop;
private SeekBar seekBar;
private LinearLayout linearLayoutRecorder, linearLayoutPlay;
private MediaRecorder mRecorder;
private MediaPlayer mPlayer;
private String fileName = null;
private int lastProgress = 0;
private Handler mHandler = new Handler();
private boolean isPlaying = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_record);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getPermissionToRecordAudio();
}
//initializingViews
initViews();
/* tx_list.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Test.this, RecordingListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
});*/
}
private void initViews() {
//tx_list=(TextView)findViewById(R.id.text_list);
linearLayoutRecorder = (LinearLayout) findViewById(R.id.linearLayoutRecorder);
chronometer = (Chronometer) findViewById(R.id.chronometerTimer);
chronometer.setBase(SystemClock.elapsedRealtime());
imageViewRecord = (ImageView) findViewById(R.id.imageViewRecord);
imageViewStop = (ImageView) findViewById(R.id.imageViewStop);
imageViewPlay = (ImageView) findViewById(R.id.imageViewPlay);
linearLayoutPlay = (LinearLayout) findViewById(R.id.linearLayoutPlay);
seekBar = (SeekBar) findViewById(R.id.seekBar);
imageViewRecord.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
imageViewStop.setOnClickListener(this);
imageViewPlay.setOnClickListener(this);
}
@RequiresApi(api = Build.VERSION_CODES.M)
public void getPermissionToRecordAudio() {
// 1) Use the support library version ContextCompat.checkSelfPermission(...) to avoid
// checking the build version since Context.checkSelfPermission(...) is only available
// in Marshmallow
// 2) Always check for permission (even if permission has already been granted)
// since the user can revoke permissions at any time through Settings
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED ) {
// The permission is NOT already granted.
// Check if the user has been asked about this permission already and denied
// it. If so, we want to give more explanation about why the permission is needed.
// Fire off an async request to actually get the permission
// This will show the standard permission request dialog UI
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE},
RECORD_AUDIO_REQUEST_CODE);
}
}
// Callback with the request from calling requestPermissions(...)
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String permissions[],
@NonNull int[] grantResults) {
// Make sure it's our original READ_CONTACTS request
if (requestCode == RECORD_AUDIO_REQUEST_CODE) {
if (grantResults.length == 3 &&
grantResults[0] == PackageManager.PERMISSION_GRANTED
&& grantResults[1] == PackageManager.PERMISSION_GRANTED
&& grantResults[2] == PackageManager.PERMISSION_GRANTED){
//Toast.makeText(this, "Record Audio permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "You must give permissions to use this app. App is exiting.", Toast.LENGTH_SHORT).show();
finishAffinity();
}
}
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View view) {
if(view == imageViewRecord){
prepareforRecording();
startRecording();
}else if(view == imageViewStop){
prepareforStop();
stopRecording();
}else if( view == imageViewPlay ){
if( !isPlaying && fileName != null ){
isPlaying = true;
startPlaying();
}else{
isPlaying = false;
stopPlaying();
}
}
}
private void prepareforRecording() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
TransitionManager.beginDelayedTransition(linearLayoutRecorder);
imageViewRecord.setVisibility(View.GONE);
imageViewStop.setVisibility(View.VISIBLE);
linearLayoutPlay.setVisibility(View.GONE);
}
}
private void startRecording() {
//we use the MediaRecorder class to record
mRecorder = new MediaRecorder();
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
/**In the lines below, we create a directory named VoiceRecorderSimplifiedCoding/Audios in the phone storage
* and the audios are being stored in the Audios folder **/
File root = android.os.Environment.getExternalStorageDirectory();
File file = new File(root.getAbsolutePath() + "/VoiceRecorderSimplifiedCoding/Audios");
if (!file.exists()) {
file.mkdirs();
}
fileName = root.getAbsolutePath() + "/VoiceRecorderSimplifiedCoding/Audios/" + String.valueOf(System.currentTimeMillis() + ".mp3");
Log.d("filename",fileName);
mRecorder.setOutputFile(fileName);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try {
mRecorder.prepare();
mRecorder.start();
} catch (IOException e) {
e.printStackTrace();
}
lastProgress = 0;
seekBar.setProgress(0);
stopPlaying();
//starting the chronometer
chronometer.setBase(SystemClock.elapsedRealtime());
chronometer.start();
}
private void stopPlaying() {
try{
mPlayer.release();
}catch (Exception e){
e.printStackTrace();
}
mPlayer = null;
//showing the play button
imageViewPlay.setImageResource(R.mipmap.play_audio);
chronometer.stop();
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
private void prepareforStop() {
TransitionManager.beginDelayedTransition(linearLayoutRecorder);
imageViewRecord.setVisibility(View.VISIBLE);
imageViewStop.setVisibility(View.GONE);
linearLayoutPlay.setVisibility(View.VISIBLE);
}
private void stopRecording() {
try{
mRecorder.stop();
mRecorder.release();
}catch (Exception e){
e.printStackTrace();
}
mRecorder = null;
//starting the chronometer
chronometer.stop();
chronometer.setBase(SystemClock.elapsedRealtime());
//showing the play button
Toast.makeText(this, "Recording saved successfully.", Toast.LENGTH_SHORT).show();
}
private void startPlaying() {
mPlayer = new MediaPlayer();
Log.d("instartPlaying",fileName);
try {
mPlayer.setDataSource(fileName);
mPlayer.prepare();
mPlayer.start();
} catch (IOException e) {
Log.e("LOG_TAG", "prepare() failed");
}
//making the imageview pause button
imageViewPlay.setImageResource(R.mipmap.play_audio);
seekBar.setProgress(lastProgress);
mPlayer.seekTo(lastProgress);
seekBar.setMax(mPlayer.getDuration());
seekUpdation();
chronometer.start();
/** once the audio is complete, timer is stopped here**/
mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
imageViewPlay.setImageResource(R.mipmap.play_audio);
isPlaying = false;
chronometer.stop();
}
});
/** moving the track as per the seekBar's position**/
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if( mPlayer!=null && fromUser ){
//here the track's progress is being changed as per the progress bar
mPlayer.seekTo(progress);
//timer is being updated as per the progress of the seekbar
chronometer.setBase(SystemClock.elapsedRealtime() - mPlayer.getCurrentPosition());
lastProgress = progress;
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
Runnable runnable = new Runnable() {
@Override
public void run() {
seekUpdation();
}
};
private void seekUpdation() {
if(mPlayer != null){
int mCurrentPosition = mPlayer.getCurrentPosition() ;
seekBar.setProgress(mCurrentPosition);
lastProgress = mCurrentPosition;
}
mHandler.postDelayed(runnable, 100);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.list_menu,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.item_list:
Intent intent = new Intent(this, RecordingListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}