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??:
- Prevent other apps from stealing my media button focus
- Detect when I've lost media button focus to another app, so I can create a new MediaSession only then, rather then whenever the active sessions change
- Check if I currently already have media button focus so I needlessly create a new MediaSession
What didn't work:
- BroadcastReceiver on AudioEffect.ACTION_OPEN_AUDIO_EFFECT_CONTROL_SESSION didn't work because apps have to manually trigger that Broadcast, and many apps, like NPR One do not
- AudioManager.OnAudioFocusChangeListener didn't work because it requires I have audio focus
- BroadcastReceiver with max priority on android.intent.action.MEDIA_BUTTON & calling abortBroadcast(), but when other apps were playing audio, my receiver wasn't triggered. Also, other apps can set max priority as well.
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!