Audio Playback Capture in Android X

Learn how to integrate the Android Playback Capture API into your app, allowing you to record and play back audio from other apps. By Evana Margain Puig.

4.8 (4) · 1 Review

Download materials
Save for later
Share

Google first introduced the Android Playback Capture API in Android 10. It allows you to capture audio from other apps, similar to screen captures or screen recording.

In this tutorial, you’ll learn how the Audio Playback Capture API works and how to use its main features. You’ll do this by integrating it into a Cat Sounds app. This app captures cat sounds on YouTube.

In the process you’ll learn how to:

  • Configure an app for Audio Playback Capture.
  • Request permission from the user to capture audio.
  • Find the captured content in your app’s files.
  • List the captured contents and play them back.

Furthermore, Audio Recording doesn’t work on emulators, so please build this tutorial on a device.

Note: This tutorial assumes that you’re familiar with the basics of Android development and Android Studio. If this is new to you, first read through the Beginning Android Development tutorial.

Getting Started

Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial. Then, open the starter project in Android Studio 4.0 or later.

You’ll find the starter project provides the interface and some logic for the Cat Sounds app already.

Build and run. You’ll see the Cat Sounds home screen with buttons to capture and stop capturing audio which, currently, does nothing. You’ll also see a tab bar at the bottom with access to a list of audio clips. This is currently just a group of mock items, but by the end of the tutorial, you’ll find your saved audio clips there.

Here’s how the Cat Sounds app looks at the moment:

Cat Sounds home screen showing a cat in a box and options to record cat sounds

What Is Audio Playback Capture API?

Suppose you want to capture some audio from an app you’re using on your smartphone. Before, the only option was to do a full-screen recording to capture the audio. But with the Audio Playback Capture API, an app can capture audio without needing to record video.

This API is useful for use cases such as live audio translation or recording and re-transmitting audio from games or apps, without having to store the data first.

Let’s get started.

Managing Permissions for Audio Capture

When you introduce a new API, you need to make changes to your project to make it work.

As you’ll be using Android Playback Capture to record data that could be sensitive, you need to ask for permission from the user first.

Requesting Permissions

Open AndroidManifest.XML. Inside, you’ll find a TODO item indicating where to add code to request permissions. Replace that with the following lines of code:

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

Those permissions are for (in order from top to bottom):

  1. Recording audio.
  2. Using the audio capture as a foreground service.
  3. Writing audio files to the phone’s memory.
  4. Reading audio files from the phone’s memory.
Note: When building your own app, you might not need all these permissions.

Checking for Permissions in Your Activity

You also have to ask for these permissions in Activities and Fragments.

Open MainActivity.kt and locate the TODO that asks you to check for permissions, then add the following code:

// 1
if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED && 
ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED &&
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {

  // 2
  val permissions = arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE, 
  Manifest.permission.READ_EXTERNAL_STORAGE)

  //3
  ActivityCompat.requestPermissions(this, permissions,0)

}

Let’s break the above code into three parts:

  1. This if checks if the user gave permissions for recording audio and writing and reading the data.
  2. If the user hasn’t granted the permissions, you add them to an array, to request them after.
  3. Once you have the array of permissions, you give it to a built-in function from Android Activity called requestPermissions. This automatically creates an alert with options for the user to grant them.

Now, you might see some warnings due to missing imports. At the top of the file, add:

import androidx.core.content.ContextCompat
import android.Manifest
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat

Now, all the errors should be gone and you can run the app with the changes you made.

Build and run; the app will display two permission dialogs. The first is for recording:

User Permission to record audio

The second asks for permission to access the device’s storage:

User permission to access photos

Changing Permissions

Sometimes, you want to test your permissions and see what happens if you deny them. To revert or change your response, go to Settings ▸ Apps and Notifications ▸ CatAudio ▸ App Permissions.

App permission screen for CatAudio

Click Microphone and you’ll see the same three options you got in the alert. You can now pick a different option.

Recording Audio From Other Apps

Now that you’ve set up the permissions, you can get back to your original purpose: capturing audio playback!

You’ll start by adding functionality to the buttons on the app’s home screen.

Open RecordFragment.kt and locate the TODO prompting you to add OnClickListeners inside of onActivityCreated. Add the following code:

button_start_recording.setOnClickListener {
  startCapturing()
}

button_stop_recording.setOnClickListener {
  stopCapturing()
}

The code above tells the app what the buttons should do when you click on them.

After adding that code, you’ll see some errors. That’s because you’re calling functions that don’t exist yet. You’ll add them next.

Find the TODO prompting you to add those methods and add the following code:

private fun startCapturing() {
  Toast.makeText(this.context, "start recording", Toast.LENGTH_LONG).show()
}

private fun stopCapturing() {
  Toast.makeText(this.context, "stop recording", Toast.LENGTH_LONG).show()
}

Currently, these two functions will only show a toast. A toast is an Android item that shows text over the screen for a couple of seconds and then disappears.

In case you have an import error add the following at the top of the file:

import android.widget.Toast
import kotlinx.android.synthetic.main.fragment_record.*

With those changes, you now know when the user taps the buttons.

Build and run, the screen should now look like the following.

Record Cat Sounds screen with the Start Recording toast

Note: If you don’t see the toast, you probably denied the permissions. Double-check that you granted permissions in the App Settings screen.

Great work! Next, you need to prompt the user for permissions one final time.

Requesting Permission Before a Capturing Session

Your next task is to request this additional permission with a prompt that displays every time the user clicks the Start Audio Capture button.

In RecordFragment.kt, you’ll find another TODO at the top to create a variable. Add this code below the class declaration to do so:

private lateinit var mediaProjectionManager: MediaProjectionManager 

Then, below the two functions you created for the listeners, create a new function:

private fun startMediaProjectionRequest() {
  // 1
  mediaProjectionManager = requireContext().getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager

  // 2
  startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(), MEDIA_PROJECTION_REQUEST_CODE)

}

The code above is a standard function to request this permission. Here’s what it does:

  1. It initializes the Media Projection Manager, which is an Android Service with all the necessary logic to capture audio.
  2. startActivityForResult is an Android method that executes when you want to get a callback after executing another activity. In this case, you execute it after the permission result.

Next, create a companion object with the MEDIA_PROJECTION_REQUEST_CODE code:

companion object {
  private const val MEDIA_PROJECTION_REQUEST_CODE = 13
}

You placed that variable in a companion object because they won’t change at any moment.

Finally, replace Toast in startCapturing() with the following:

if (!isRecordAudioPermissionGranted()) {
  requestRecordAudioPermission()
} else {
  startMediaProjectionRequest()
}

The code above checks for audio record permissions. If the user authorized them, you start the audio capture.

Note: At this point, you may wonder why you’re asking for permissions again if you already had them for the MainActivity. In the latest versions of Android, the OS is much stricter with permissions and personal data, so every time you use a service that requires permissions, it’s necessary to check again.

Next, you’ll make sure that your app has the necessary permissions before proceeding. You will soon understand all the pieces you just put together. For now, go ahead and check the permissions: