Create an Android Sound Recorder using Kotlin

Create an Android Sound Recorder using Kotlin

The Android multimedia framework includes support for recording and playing audio. In this blog post, we’ll develop a basic Sound Recorder application that is capable of recording audio and saving it into the local storage of an Android device using the MediaRecorder that is provided by the Android SDK.

You will also learn how to request user-permissions in real-time and how to work with the local storage of an Android device.

Creating the UI

First, we need to build the user interface of the Audio Recorder. It consists of a simple layout with 3 buttons which will be used to start, pause, resume and stop the recording.

<?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"
        tools:context=".MainActivity">

    <TextView
            android:id="@+id/textview_sound_recorder_heading"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Sound Recorder"
            android:layout_centerHorizontal="true"
            android:textSize="32dp"
            android:textStyle="bold"
            android:textColor="#000"
            android:layout_marginTop="32dp"
    />

    <Button
            android:id="@+id/button_start_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Start"
            android:layout_alignParentBottom="true"
            android:layout_marginLeft="32dp"
            android:layout_marginBottom="32dp"
            android:layout_centerVertical="true"/>

    <Button
            android:id="@+id/button_pause_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Pause"
            android:layout_alignParentBottom="true"
            android:layout_centerHorizontal="true"
            android:layout_marginBottom="32dp"/>

    <Button
            android:id="@+id/button_stop_recording"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Stop"
            android:layout_alignParentBottom="true"
            android:layout_alignParentRight="true"
            android:layout_marginBottom="32dp"
            android:layout_marginRight="32dp"/>
</RelativeLayout>

Requesting the required permissions

After creating the UI we can almost start using the MediaRecorder to build our app. But first, we need to request the required permissions to record the audio and access the local storage. We can do that with some simple lines of code in our AndroidManifest.xml file.

<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

We also need to check if the user has really enabled the permissions before we can use our MediaRecorder. We can do so in our MainActivity.kt file.

if (ContextCompat.checkSelfPermission(this,
      Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
      Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
  val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
  ActivityCompat.requestPermissions(this, permissions,0)
}

Note: This lines of code will later be moved in the OnClickListener call of the start_recording button so we can make sure that the MediaRecorder will not be started without having the right permissions.

Recording and saving audio

Adding OnClickListeners

First, we need to add OnClickListeners to the buttons to make sure they react to user events. As I mentioned before the check for the right permissions will be added in the OnClickListener call of the start_recording button.

button_start_recording.setOnClickListener {
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
        val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
        ActivityCompat.requestPermissions(this, permissions,0)
    } else {
        startRecording()
    }
}

button_stop_recording.setOnClickListener{
    stopRecording()
}

button_pause_recording.setOnClickListener {
    pauseRecording()
}

Configuring the MediaRecorder

Next, we need to define a path for our output and start configuring our MediaRecorder.

private var output: String? = null
private var mediaRecorder: MediaRecorder? = null
private var state: Boolean = false
private var recordingStopped: Boolean = false

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"
    mediaRecorder = MediaRecorder()
    
    mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
    mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
    mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
    mediaRecorder?.setOutputFile(output)
}

Here, we get the path to the root of our external storage and add our recording name and filetype to it. After that, we create our MediaRecorder object and define the audio source, audio encoder, output format and output file.

Recording and saving the audio

The code used to start the MediaRecorder is defined in the OnClickListener of the start_recording button.

private fun startRecording() {
    try {
        mediaRecorder?.prepare()
        mediaRecorder?.start()
        state = true
        Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
    } catch (e: IllegalStateException) {
        e.printStackTrace()
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

As you can see we need to call the prepare function before we can start the recording. We also embed it into a try-catch block to make sure the app won’t crash when the prepares function fails.

The OnClickListeners of the stop button is very similar to the one we defined above.

private fun stopRecording(){
    if(state){
        mediaRecorder?.stop()
        mediaRecorder?.release()
        state = false
    }else{
        Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
    }
}

Here we check if the MediaRecorder is currently running before we actually stop the recording because our app would crash if the stop method is called while the MediaRecorder isn’t recording. After that, we change the state variable to false to prevent the user from pressing the stop button again.

After that, we just need to define the OnClickListener for the pause/resume button.

@SuppressLint("RestrictedApi", "SetTextI18n")
@TargetApi(Build.VERSION_CODES.N)
private fun pauseRecording() {
    if(state) {
        if(!recordingStopped){
            Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
            mediaRecorder?.pause()
            recordingStopped = true
            button_pause_recording.text = "Resume"
        }else{
            resumeRecording()
        }
    }
}

@SuppressLint("RestrictedApi", "SetTextI18n")
@TargetApi(Build.VERSION_CODES.N)
private fun resumeRecording() {
    Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
    mediaRecorder?.resume()
    button_pause_recording.text = "Pause"
    recordingStopped = false
}

In these two methods we check if the MediaRecorder is running. If so we pause the recording and change the text of the button to resume. If clicked again the recording will resume from the point it left of.

Finally, we can start recording audio and listen to it by opening the recording.mp3 file that will be saved in our local storage.

Complete Source Code for the MainActivity.kt

Here you can get the complete source code for our basic Sound Recorder app:

package com.example.android.soundrecorder

import android.Manifest
import android.annotation.SuppressLint
import android.annotation.TargetApi
import android.content.pm.PackageManager
import android.media.MediaRecorder
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Environment
import android.support.v4.app.ActivityCompat
import android.support.v4.content.ContextCompat
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import java.io.IOException

class MainActivity : AppCompatActivity() {

    private var output: String? = null
    private var mediaRecorder: MediaRecorder? = null
    private var state: Boolean = false
    private var recordingStopped: Boolean = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        mediaRecorder = MediaRecorder()
        output = Environment.getExternalStorageDirectory().absolutePath + "/recording.mp3"

        mediaRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)
        mediaRecorder?.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
        mediaRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
        mediaRecorder?.setOutputFile(output)

        button_start_recording.setOnClickListener {
            if (ContextCompat.checkSelfPermission(this,
                    Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                val permissions = arrayOf(android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                ActivityCompat.requestPermissions(this, permissions,0)
            } else {
                startRecording()
            }
        }

        button_stop_recording.setOnClickListener{
            stopRecording()
        }

        button_pause_recording.setOnClickListener {
            pauseRecording()
        }
    }

    private fun startRecording() {
        try {
            mediaRecorder?.prepare()
            mediaRecorder?.start()
            state = true
            Toast.makeText(this, "Recording started!", Toast.LENGTH_SHORT).show()
        } catch (e: IllegalStateException) {
            e.printStackTrace()
        } catch (e: IOException) {
            e.printStackTrace()
        }
    }

    @SuppressLint("RestrictedApi", "SetTextI18n")
    @TargetApi(Build.VERSION_CODES.N)
    private fun pauseRecording() {
        if(state) {
            if(!recordingStopped){
                Toast.makeText(this,"Stopped!", Toast.LENGTH_SHORT).show()
                mediaRecorder?.pause()
                recordingStopped = true
                button_pause_recording.text = "Resume"
            }else{
                resumeRecording()
            }
        }
    }

    @SuppressLint("RestrictedApi", "SetTextI18n")
    @TargetApi(Build.VERSION_CODES.N)
    private fun resumeRecording() {
        Toast.makeText(this,"Resume!", Toast.LENGTH_SHORT).show()
        mediaRecorder?.resume()
        button_pause_recording.text = "Pause"
        recordingStopped = false
    }

    private fun stopRecording(){
        if(state){
            mediaRecorder?.stop()
            mediaRecorder?.release()
            state = false
        }else{
            Toast.makeText(this, "You are not recording right now!", Toast.LENGTH_SHORT).show()
        }
    }
}

Looking up the audio file

After recording you need to go into the local storage of your Android device to listen to your recordings. I’m just including it for the people who aren’t confident navigating in the local Android storage.

  1. Open the files app on your Android device
  2. Go to the local register
  3. Search for recording.mp3

Closing Notes

Now you should know how the MediaRecorder works, how you can request permission in real-time and why it is important to do so. You have also learned about the local storage of your Android device and how you store data in it.

A more complex version of this app which has some extra features like playing your recordings using the MediaPlayer is also available on my Github.

If you have found this useful, please consider recommending and sharing it with other fellow developers.

If you have any questions or critics, you can reach me in the comment section.

Read these next: