How to Manage Audio Focus in Android?
The audio focus in Android needs to be managed and it is one of the important to handle the audio interruptions. In android, many applications play media simultaneously, and to increase the User Experience the Audio interruptions are handled. For example, if the application is playing Audio, suddenly there is an incoming call then the audio file needs to be stopped and after the call is ended the Audio should continue playing from where it has stopped. In this article, it’s been discussed how to handle the Audio interruptions or how to implement the Audio Focus in Android. Have a look at the following image to get an idea of what things are going to be discussed. Note that we are going to implement this project using the Java language.
Steps to Implement the Audio Focus or handle Audio interruptions
Step 1: Create an empty activity project
- Create an empty activity Android Studio project. And select Java as a programming language.
- To know how to create an empty activity Android Studio project refer to Android | How to Create/Start a New Project in Android Studio?
Step 2: Working with the activity_main.xml file
- The main layout in this project includes only three buttons which are used to play, pause, and to stop the Audio file playing in the application.
- Invoke the following code to implement the UI.
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" tools:context = ".MainActivity" tools:ignore = "HardcodedText" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerHorizontal = "true" android:layout_marginStart = "16dp" android:layout_marginTop = "32dp" android:layout_marginEnd = "16dp" android:text = "Manage Audio Focus (Handling Audio interruptions in Android" android:textSize = "18sp" android:textStyle = "bold" /> < LinearLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginTop = "100dp" android:gravity = "center_horizontal" android:orientation = "horizontal" > < Button android:id = "@+id/stopButton" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginEnd = "32dp" android:backgroundTint = "@color/colorPrimary" android:drawableStart = "@drawable/ic_stop" android:text = "STOP" android:textColor = "@android:color/white" /> < Button android:id = "@+id/playButton" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginEnd = "32dp" android:backgroundTint = "@color/colorPrimary" android:drawableStart = "@drawable/ic_play" android:text = "PLAY" android:textColor = "@android:color/white" /> < Button android:id = "@+id/pasueButton" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:backgroundTint = "@color/colorPrimary" android:drawableStart = "@drawable/ic_pause" android:text = "PAUSE" android:textColor = "@android:color/white" /> </ LinearLayout > </ RelativeLayout > |
Output UI:
Step 3: Working with the MainActivity.java file
- The main Callback needs to implemented when there is a change in the Audio focus. Meaning the system has transferred the audio focus to another service that is used by the app, in this case, the Phone application which takes the audio focus from the current application which is playing the audio.
AudioManager.OnAudioFocusChangeListener audioFocusChangeListener -> This handles if there is change in the audio focus which the callback need to implemented according to the focus change from the audio manage.
- When the call gets hanged up the focus changes to the current application and MediaPlayer is resumed.
- The focus request result which is returned by the Android system is compared to the following constants.
- AUDIOFOCUS_GAIN: if the system grants the audio focus gain, then the playback can be continued after the temporary loss of the audio focus.
- AUDIOFOCUS_LOSS_TRANSIENT: if there is temporary loss of audio focus then the playback of the audio should be paused.
- AUDIOFOCUS_LOSS: if there is permanent loss of the audio then the mediaplayer should be released (completely stopped).
- To implement the Audio focus in the application same as discussed above invoke the following code in the MainActivity.java file. Comments are added for better understanding.
Java
import androidx.annotation.RequiresApi; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.media.MediaPlayer; import android.os.Build; import android.os.Bundle; import android.view.View; import android.widget.Button; import java.io.IOException; @RequiresApi (api = Build.VERSION_CODES.O) public class MainActivity extends AppCompatActivity { // media player instance to playback // the media file from the raw folder MediaPlayer mediaPlayer; // Audio manager instance to manage or // handle the audio interruptions AudioManager audioManager; // Audio attributes instance to set the playback // attributes for the media player instance // these attributes specify what type of media is // to be played and used to callback the audioFocusChangeListener AudioAttributes playbackAttributes; // media player is handled according to the // change in the focus which Android system grants for AudioManager.OnAudioFocusChangeListener audioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { @Override public void onAudioFocusChange( int focusChange) { if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { mediaPlayer.start(); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { mediaPlayer.pause(); mediaPlayer.seekTo( 0 ); } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { mediaPlayer.release(); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); // get the audio system service for // the audioManger instance audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); // initiate the audio playback attributes playbackAttributes = new AudioAttributes.Builder() .setUsage(AudioAttributes.USAGE_GAME) .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .build(); // set the playback attributes for the focus requester AudioFocusRequest focusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN) .setAudioAttributes(playbackAttributes) .setAcceptsDelayedFocusGain( true ) .setOnAudioFocusChangeListener(audioFocusChangeListener) .build(); // request the audio focus and // store it in the int variable final int audioFocusRequest = audioManager.requestAudioFocus(focusRequest); // register all three buttons Button bPlay = findViewById(R.id.playButton); Button bPause = findViewById(R.id.pasueButton); Button bStop = findViewById(R.id.stopButton); // initiate the media player instance with // the media file from the raw folder mediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.music); // handle the PLAY button to play the audio bPlay.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { // request the audio focus by the Android system // if the system grants the permission // then start playing the audio file if (audioFocusRequest == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { mediaPlayer.start(); } } }); // handle the PAUSE button to pause the media player bPause.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { mediaPlayer.pause(); } }); // handle the STOP button to stop the media player bStop.setOnClickListener( new View.OnClickListener() { @Override public void onClick(View v) { mediaPlayer.stop(); try { // if the mediaplayer is stopped then // it should be again prepared for // next instance of play mediaPlayer.prepare(); } catch (IOException e) { e.printStackTrace(); } } }); } } |
Note: When there is no requirement of audio focus, abandonAudioFocusRequest method needs to be called with the AudioManager instance and it requires the parameter AudioFocusRequest focusRequest which needs to be passed.