Background Info: I need to detect whenever a user presses the play/pause button found on most headsets (KEYCODE_MEDIA_PLAY_PAUSE).
I have it all mostly working using MediaSessions, but when another app starts playing audio, I stop getting callbacks.
It seems like this is because the app that's playing audio created its own MediaSession and Android sends KeyEvents only to the newest MediaSession. To prevent this I create an OnActiveSessionsChangedListener and create a new MediaSession every time it fires.
This does work, but every time I create a new MediaSession, the listener fires again, so I find myself stuck in an inf loop.
My Question: does anyone know how I can do any of the following??:
What didn't work:
My Code:
mMediaSessionManager.addOnActiveSessionsChangedListener(controllers -> {
boolean updateButtonReceiver = false;
// recreate MediaSession if another app handles media buttons
for (MediaController mediaController : controllers) {
if (!TextUtils.equals(getPackageName(), mediaController.getPackageName())) {
if ((mediaController.getFlags() & (MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS)) != 0L) {
updateButtonReceiver = true;
}
}
}
if (updateButtonReceiver) {
// using a handler with a delay of about 2 seconds because this listener fires very often.
mAudioFocusHandler.removeCallbacksAndMessages(null);
mAudioFocusHandler.sendEmptyMessageDelayed(0, AUDIO_FOCUS_DELAY_MS);
}
}, ClickAppNotificationListener.getComponentName(this));
Here is the handler that gets triggered:
private final Handler mAudioFocusHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (mShouldBeEnabled) {
updateButtonReceiverEnabled(true);
}
}
};
And finally here is the method that the Handler triggers:
private void updateButtonReceiverEnabled(boolean shouldBeEnabled) {
// clear old session (not sure if this is necessary)
if (mMediaSession != null) {
mMediaSession.setActive(false);
mMediaSession.setFlags(0);
mMediaSession.setCallback(null);
mMediaSession.release();
mMediaSession = null;
}
mMediaSession = new MediaSessionCompat(this, MEDIA_SESSION_TAG);
mMediaSession.setCallback(mMediaButtonCallback);
mMediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mMediaSession.setPlaybackToLocal(AudioManager.STREAM_MUSIC);
mMediaSession.setActive(true);
mMediaSession.setPlaybackState(new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE)
.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 0f)
.build());
if (shouldBeEnabled != mShouldBeEnabled) {
getPackageManager().setComponentEnabledSetting(mMediaButtonComponent,
shouldBeEnabled
? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
mShouldBeEnabled = shouldBeEnabled;
}
Thanks!
Your media session callbacks call methods in several APIs to control the player, manage the audio focus, and communicate with the media session and media browser service.
Media buttons are hardware buttons found on Android devices and other peripheral devices, for example, the pause/play button on a Bluetooth headset. When a user presses a media button, Android generates a KeyEvent , which contains a key code that identifies the button.
Media browser services enable applications to browse media content provided by an application and ask the application to start playing it. They may also be used to control content that is already playing by way of a MediaSession .
android.media.session.MediaSession. Allows interaction with media controllers, volume keys, media buttons, and transport controls. A MediaSession should be created when an app wants to publish media playback information or handle media keys.
if you just want to capture MediaButton
you can register a BroadcastReceiver
to get Media Button action all the time .
MediaButtonIntentReceiver class :
public class MediaButtonIntentReceiver extends BroadcastReceiver {
public MediaButtonIntentReceiver() {
super();
}
@Override
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
if (!Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
return;
}
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event == null) {
return;
}
int action = event.getAction();
if (action == KeyEvent.ACTION_DOWN) {
// do something
Toast.makeText(context, "BUTTON PRESSED!", Toast.LENGTH_SHORT).show();
}
abortBroadcast();
}
}
add this to manifest.xml:
<receiver android:name=".MediaButtonIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
and register your BroadcastReceiver
like this ( in main activity)
IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
MediaButtonIntentReceiver r = new MediaButtonIntentReceiver();
filter.setPriority(1000);
registerReceiver(r, filter);
also look at :
How to capture key events from bluetooth headset with android
How do I intercept button presses on the headset in Android?
The controllers
you get in OnActiveSessionsChangedListener
is ordered by priority. You only have to create a new MediaSession
if you see that your MediaSession
is not the first one in the list.
Note that you might still run into an infinite loop if there is another app contending the media key events using the same approach.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With