Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

App does not become the preferred MediaButtonReceiver when calling setActive(true) a second time

I have tried to implement media button control per Google's talk

I set the receiver in manifest:

<receiver android:name="android.support.v4.media.session.MediaButtonReceiver">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

<service android:name=".player.PlayFileService">
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</service>

create the MediaSessionCompat in onCreate() of Service:

    mediaSession = new MediaSessionCompat(getApplicationContext(), "SOUNDPROCESS");
    mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    PlaybackStateCompat ps = new PlaybackStateCompat.Builder()
            .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY_PAUSE)
            .build();
    mediaSession.setPlaybackState(ps);
    mediaSession.setCallback(new MediaSessionCompat.Callback() {
        @Override
        public void onPlay() { ...
           }
    });

and handle the intent in onStartCommand()

MediaButtonReceiver.handleIntent(mediaSession, intent);

I then call setActive(true); when I gain audio focus, and setActive(false); when I stop playback. This works the first time, my app becomes the preferred media button receiver and receives callbacks.

However, if I stop playback in my app, go into another app like Google Play Music and start playback there, then return to my app and call setActive(true) Google Play Music continues to receive the callback and the media buttons don't work in my app.

From my understanding, the last call to setActive(true) should take precedence. I have verified that isActive() returns true. I can also work around the issue by creating a new MediaSessionCompat each time, but this doesn't seem ideal.

How can I make my app become the preferred media button receiver every time I call setActive(true)?

UPDATE: Minimum project to reproduce the problem here: https://github.com/svenoaks/MediaButtonDemo.git

Steps to reproduce:

  1. Run app, press the PLAY button. setActive(true) is called and MediaButtonReceiver is now the preferred media button.
  2. Press play/pause button on wired or wireless headphones or other media button. Toast shows indicating that callback is working.
  3. Start playback on another app such as Google Play Music which supports media buttons. Press pause on this app.
  4. The demo app can no longer be the preferred media button receiver, even if PLAY button is pressed again, calling setActive(true) again. The other app always responds to media buttons.

This was tested on Android 6.0.

like image 620
Steve M Avatar asked Jan 22 '17 20:01

Steve M


2 Answers

My observations: (No enough reputations to comment, apologies, but I can reply to the comments for this answer)

  1. 'Run app, press the PLAY button. setActive(true) is called and MediaButtonReceiver is now the preferred media button': because Android first dispatches media button events to the foreground activity.

  2. 'Start playback on another app.......Press pause on this app........then return to my app and call setActive(true) ':

a) if you had called onStop listener before playing GooglePlay Music, then your media session is not active (until onPlay is called again) when you press any media button, and so the current session which is Google Play gets played.

b) If you hadn't called onStop then there are 2 active sessions (the first session of your app and the first session of Google Play. Second session won't be called unless OnPlay is called again). According to docs, if there are multiple active media sessions, Android tries to choose a media session that is preparing to play (buffering/connecting), playing, or paused, rather than one that is stopped.' So GooglePlay is the latest active session with paused state and gets played.

c) If you had called onStop (thus setActive(false)), and if GooglePlayMusic had also setActive(false), and you haven't clicked onPlay again then in that case too GooglePlayMusic will be played since "If there is no active session, Android tries to send the event to the most recently active session. On Android 5.0 (API level 21) or later the event is only sent to sessions that have called setMediaButtonReceiver()."

Have you setMediaButtonReceiver()?

https://developer.android.com/guide/topics/media-apps/mediabuttons.html Kindly have a look at it

like image 81
Vyshnav Ramesh Thrissur Avatar answered Nov 08 '22 19:11

Vyshnav Ramesh Thrissur


The key to making this work correctly is to correctly set the PlaybackState every time your app starts playing or pauses (which it should be doing when another app gains the audio session and activates its own MediaSession. Without this, the problem as above will happen. Also, for Android 4.4 and lower, FLAG_HANDLES_TRANSPORT_CONTROLS must be set or the same problem will happen.

like image 2
Steve M Avatar answered Nov 08 '22 18:11

Steve M