Last active
August 28, 2023 10:14
-
-
Save kuccello/5816882 to your computer and use it in GitHub Desktop.
An example MusicPlayer class that demonstrates how to use AudioFocus and a BroadcastReceiver to play audio cleanly on Android.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:tools="http://schemas.android.com/tools" | |
android:id="@+id/LinearLayout1" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" | |
android:orientation="vertical" | |
android:paddingBottom="@dimen/activity_vertical_margin" | |
android:paddingLeft="@dimen/activity_horizontal_margin" | |
android:paddingRight="@dimen/activity_horizontal_margin" | |
android:paddingTop="@dimen/activity_vertical_margin" | |
tools:context=".MainActivity" > | |
<TextView | |
android:id="@+id/textView1" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:text="@string/app_message" | |
android:textAppearance="?android:attr/textAppearanceLarge" /> | |
<LinearLayout | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_gravity="center_vertical|center_horizontal|center" > | |
<Button | |
android:id="@+id/btn_playpause" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:onClick="playpause" | |
android:text="@string/play" /> | |
<Button | |
android:id="@+id/btn_stop" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:onClick="stop" | |
android:text="@string/stop" /> | |
</LinearLayout> | |
</LinearLayout> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2013 Google Inc. All Rights Reserved. | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
*/ | |
package tv.screenintensity.propermusicplayback; | |
import android.app.Activity; | |
import android.os.Bundle; | |
import android.view.View; | |
import android.widget.Button; | |
public class MainActivity extends Activity { | |
private static MusicPlayer mMusicPlayer; | |
@Override | |
protected void onCreate(Bundle savedInstanceState) { | |
super.onCreate(savedInstanceState); | |
setContentView(R.layout.activity_main); | |
} | |
public void playpause(View v) { | |
if (mMusicPlayer == null) { | |
mMusicPlayer = MusicPlayer.getInstance(this); | |
} | |
Button button = (Button) v; | |
if (button.getText().equals(getResources().getString(R.string.play))) { | |
mMusicPlayer.play(); | |
button.setText(getResources().getString(R.string.pause)); | |
} else { | |
mMusicPlayer.pause(); | |
button.setText(getResources().getString(R.string.play)); | |
} | |
} | |
public void stop(View v) { | |
mMusicPlayer.stop(); | |
Button button = (Button) findViewById(R.id.btn_playpause); | |
button.setText(getResources().getString(R.string.play)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Copyright 2013 Google Inc. All Rights Reserved. | |
Licensed under the Apache License, Version 2.0 (the "License"); | |
you may not use this file except in compliance with the License. | |
You may obtain a copy of the License at | |
http://www.apache.org/licenses/LICENSE-2.0 | |
Unless required by applicable law or agreed to in writing, software | |
distributed under the License is distributed on an "AS IS" BASIS, | |
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
See the License for the specific language governing permissions and | |
limitations under the License. | |
*/ | |
package tv.screenintensity.propermusicplayback; | |
import android.content.BroadcastReceiver; | |
import android.content.Context; | |
import android.content.Intent; | |
import android.content.IntentFilter; | |
import android.media.AudioManager; | |
import android.media.MediaPlayer; | |
import android.util.Log; | |
/** | |
* This music player class illustrates some best practices with respect to audio | |
* focus and audio play back. | |
*/ | |
public class MusicPlayer { | |
private static final String TAG = "BEST PRACTICES MUSIC PLAYER"; | |
private static MusicPlayer sInstance; | |
private static final String CMD_NAME = "command"; | |
private static final String CMD_PAUSE = "pause"; | |
private static final String CMD_STOP = "pause"; | |
private static final String CMD_PLAY = "play"; | |
// Jellybean | |
private static String SERVICE_CMD = "com.sec.android.app.music.musicservicecommand"; | |
private static String PAUSE_SERVICE_CMD = "com.sec.android.app.music.musicservicecommand.pause"; | |
private static String PLAY_SERVICE_CMD = "com.sec.android.app.music.musicservicecommand.play"; | |
// Honeycomb | |
{ | |
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) { | |
SERVICE_CMD = "com.android.music.musicservicecommand"; | |
PAUSE_SERVICE_CMD = "com.android.music.musicservicecommand.pause"; | |
PLAY_SERVICE_CMD = "com.android.music.musicservicecommand.play"; | |
} | |
}; | |
public static MusicPlayer getInstance(Context context) { | |
if (sInstance == null) { | |
sInstance = new MusicPlayer(context); | |
} | |
return sInstance; | |
} | |
private Context mContext; | |
private boolean mAudioFocusGranted = false; | |
private boolean mAudioIsPlaying = false; | |
private MediaPlayer mPlayer; | |
private AudioManager.OnAudioFocusChangeListener mOnAudioFocusChangeListener; | |
private BroadcastReceiver mIntentReceiver; | |
private boolean mReceiverRegistered = false; | |
private MusicPlayer(Context context) { | |
mContext = context; | |
mOnAudioFocusChangeListener = new AudioManager.OnAudioFocusChangeListener() { | |
@Override | |
public void onAudioFocusChange(int focusChange) { | |
switch (focusChange) { | |
case AudioManager.AUDIOFOCUS_GAIN: | |
Log.i(TAG, "AUDIOFOCUS_GAIN"); | |
play(); | |
break; | |
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT: | |
Log.i(TAG, "AUDIOFOCUS_GAIN_TRANSIENT"); | |
break; | |
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK: | |
Log.i(TAG, "AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK"); | |
break; | |
case AudioManager.AUDIOFOCUS_LOSS: | |
Log.e(TAG, "AUDIOFOCUS_LOSS"); | |
pause(); | |
break; | |
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: | |
Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT"); | |
pause(); | |
break; | |
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: | |
Log.e(TAG, "AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK"); | |
break; | |
case AudioManager.AUDIOFOCUS_REQUEST_FAILED: | |
Log.e(TAG, "AUDIOFOCUS_REQUEST_FAILED"); | |
break; | |
default: | |
// | |
} | |
} | |
}; | |
} | |
public void play() { | |
if (!mAudioIsPlaying) { | |
if (mPlayer == null) { | |
mPlayer = MediaPlayer.create(mContext, R.raw.krispy_mixdown); | |
mPlayer.setLooping(true); | |
} | |
// 1. Acquire audio focus | |
if (!mAudioFocusGranted && requestAudioFocus()) { | |
// 2. Kill off any other play back sources | |
forceMusicStop(); | |
// 3. Register broadcast receiver for player intents | |
setupBroadcastReceiver(); | |
} | |
// 4. Play music | |
mPlayer.start(); | |
mAudioIsPlaying = true; | |
} | |
} | |
public void pause() { | |
// 1. Suspend play back | |
if (mAudioFocusGranted && mAudioIsPlaying) { | |
mPlayer.pause(); | |
mAudioIsPlaying = false; | |
} | |
} | |
public void stop() { | |
// 1. Stop play back | |
if (mAudioFocusGranted && mAudioIsPlaying) { | |
mPlayer.stop(); | |
mPlayer = null; | |
mAudioIsPlaying = false; | |
// 2. Give up audio focus | |
abandonAudioFocus(); | |
} | |
} | |
private boolean requestAudioFocus() { | |
if (!mAudioFocusGranted) { | |
AudioManager am = (AudioManager) mContext | |
.getSystemService(Context.AUDIO_SERVICE); | |
// Request audio focus for play back | |
int result = am.requestAudioFocus(mOnAudioFocusChangeListener, | |
// Use the music stream. | |
AudioManager.STREAM_MUSIC, | |
// Request permanent focus. | |
AudioManager.AUDIOFOCUS_GAIN); | |
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { | |
mAudioFocusGranted = true; | |
} else { | |
// FAILED | |
Log.e(TAG, | |
">>>>>>>>>>>>> FAILED TO GET AUDIO FOCUS <<<<<<<<<<<<<<<<<<<<<<<<"); | |
} | |
} | |
return mAudioFocusGranted; | |
} | |
private void abandonAudioFocus() { | |
AudioManager am = (AudioManager) mContext | |
.getSystemService(Context.AUDIO_SERVICE); | |
int result = am.abandonAudioFocus(mOnAudioFocusChangeListener); | |
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { | |
mAudioFocusGranted = false; | |
} else { | |
// FAILED | |
Log.e(TAG, | |
">>>>>>>>>>>>> FAILED TO ABANDON AUDIO FOCUS <<<<<<<<<<<<<<<<<<<<<<<<"); | |
} | |
mOnAudioFocusChangeListener = null; | |
} | |
private void setupBroadcastReceiver() { | |
mIntentReceiver = new BroadcastReceiver() { | |
@Override | |
public void onReceive(Context context, Intent intent) { | |
String action = intent.getAction(); | |
String cmd = intent.getStringExtra(CMD_NAME); | |
Log.i(TAG, "mIntentReceiver.onReceive " + action + " / " + cmd); | |
if (PAUSE_SERVICE_CMD.equals(action) | |
|| (SERVICE_CMD.equals(action) && CMD_PAUSE.equals(cmd))) { | |
play(); | |
} | |
if (PLAY_SERVICE_CMD.equals(action) | |
|| (SERVICE_CMD.equals(action) && CMD_PLAY.equals(cmd))) { | |
pause(); | |
} | |
} | |
}; | |
// Do the right thing when something else tries to play | |
if (!mReceiverRegistered) { | |
IntentFilter commandFilter = new IntentFilter(); | |
commandFilter.addAction(SERVICE_CMD); | |
commandFilter.addAction(PAUSE_SERVICE_CMD); | |
commandFilter.addAction(PLAY_SERVICE_CMD); | |
mContext.registerReceiver(mIntentReceiver, commandFilter); | |
mReceiverRegistered = true; | |
} | |
} | |
private void forceMusicStop() { | |
AudioManager am = (AudioManager) mContext | |
.getSystemService(Context.AUDIO_SERVICE); | |
if (am.isMusicActive()) { | |
Intent intentToStop = new Intent(SERVICE_CMD); | |
intentToStop.putExtra(CMD_NAME, CMD_STOP); | |
mContext.sendBroadcast(intentToStop); | |
} | |
} | |
} |
Does this works on Api 19 and above?
thank you
Thank you, this code helped a lot
Thaank youuuuu
Thank you this code help me a lot, greeting from Ethiopia
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks. Should we unregister the receiver later?