androidandroid-5.0-lollipopevent-receiver

WebView control with focus blocks media button events in Android 5


Edit May 8, 2016

I found the reason for my troubles receiving media button events in my app. See the answer below. I editid the title of this question to make finding the issue easier. The original title was "What can possibly block media buttons on Android Lollipop".

Original question, April 2015:

Scratching my head and staring at all the code for 2 days to no avail... My Android app is supposed to react to media buttons (e.g. from a headset, test it with a Bluetooth headset), like play/pause, next, rewind. Works fine on KitKat and below. I swear it even worked on Lollipop as well up until a few days ago. Now nothing, no trace that it hears media button presses. Would anyone have a quick suggestion where to look for the trouble? Tested with two Lollipop phones, same Bluetooth headset, and the same headset works fine for lower versions of Android. Also the same headset works fine, media buttons presses heard in other apps. What possibly could I break???

I now tested both old and new ways of listening to media buttons. In AndroidManifest.xml:

<receiver android:name=".MediaButtonIntentReceiver" android:enabled="false">
  <intent-filter>
      <action android:name="android.intent.action.MEDIA_BUTTON" />
  </intent-filter>
</receiver>

The fact that it says enabled="false" is OK - I enable and disable the receiver as needed, and the MediaButtonIntentReceiver.java gets the events fine on KitKat and lower, complete silence on Lollipop.

I next switched to the latest appcompat (v22.1) and tried using MediaSessionCompat object and related code as follows. This worked great in a small test app, just one activity that I wrote - got my Logcat messages confirming that it hears media keys pressed on Lollipop. But when inserted into my app, again does not work on Lollipop. What the heck???

private MediaSessionCompat _mediaSession;
final String MEDIA_SESSION_TAG = "some_tag";

void initMediaSessions(Context context) {
    // Start a new MediaSession
    if (context == null)
        return;
    Lt.d("initMediaSession()...");
    ComponentName eventReceiver = new ComponentName(context.getPackageName(), MediaButtonIntentReceiver.class.getName());
    PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(
            context,
            0,
            new Intent(Intent.ACTION_MEDIA_BUTTON),
            PendingIntent.FLAG_UPDATE_CURRENT
    );

    // Parameters for new MediaSessionCompat():
    // context  The context.
    // tag  A short name for debugging purposes.
    // mediaButtonEventReceiver     The component name for your receiver. This must be non-null to support platform
    // versions earlier than LOLLIPOP. May not be null below Lollipop.
    // mbrIntent    The PendingIntent for your receiver component that handles media button events. This is optional
    // and will be used on JELLY_BEAN_MR2 and later instead of the component name.
    _mediaSession = new MediaSessionCompat(context, MEDIA_SESSION_TAG, eventReceiver, buttonReceiverIntent);
    _mediaSession.setCallback(new MediaSessionCallback());
    _mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
                            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
    _mediaSession.setActive(true);
    PlaybackStateCompat state = new PlaybackStateCompat.Builder()
            .setActions(
                    PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PLAY_PAUSE |
                            PlaybackStateCompat.ACTION_PAUSE |
                            PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)
            .setState(PlaybackStateCompat.STATE_STOPPED, 0, 1, SystemClock.elapsedRealtime())
            .build();
    _mediaSession.setPlaybackState(state);
}


final class MediaSessionCallback extends MediaSessionCompat.Callback {
    @Override
    public void onPlay() {
        Lt.d("play");
    }

    @Override
    public void onPause() {
        Lt.d("pause");
    }

    @Override
    public void onStop() {
        Lt.d("stop.");
    }

    @Override
    public void onSkipToNext() {
        Lt.d("skipToNext");
    }

    @Override
    public void onSkipToPrevious() {
        Lt.d("skipToPrevious");
    }

    @Override
    public boolean onMediaButtonEvent(final Intent mediaButtonIntent) {
        Lt.d("GOT MediaButton EVENT");
        KeyEvent keyEvent = (KeyEvent) mediaButtonIntent.getExtras().get(Intent.EXTRA_KEY_EVENT);
        // ...do something with keyEvent, super... does nothing.
        return super.onMediaButtonEvent(mediaButtonIntent);
    }
}

Solution

  • May 8, 2016: Solved for sure I finally found the reason why media buttons did not work with my app, when the main screen was on. I later noticed that they worked if any other app was on active, or screen turned off etc. - but not when my main activity was up. Turns out that in Android 5 at least, the WebView control I use on my main screen blocks the media buttons, if it has focus. Calling:

    webView.setFocusable(false);
    

    or

    webView.setFocusableInTouchMode(false);
    

    or setting the same attribute in the layout, solves the problem. With Android 6 the focusable setting of WebView did not matter for receiving media button events.

    Apr. 24, 2015: Solved? - not quite... From the comment to this post: Handling media buttons in Android 5.0 Lollipop by @mangini:

    To receive media button events while your activity is in foreground, call setMediaController() on your activity with a MediaController instance connected to your active MediaSession. – mangini Jan 23 at 18:35

    I was testing all the time with screen on and my activity opened on it, as it always worked that way under older versions of Android too... As soon as I pressed the home button, or turned off the screen, my old MediaButtonIntentReceiver started working normally.

    Update Media button events come to my app only if my lock screen manager, using the depreciated RemoteControlClient is enabled, and of course my activity is not in foreground. If I disable the lock screen manager (which is an option in my app) and delete RemoteControlClient, again, I can't get any media events.

    This issue under Lollipop is a complete mess. Or at least a complete mess about it exists in my mind right now. Wish someone posted a clear code sample for handling media buttons, that would work under older versions of Android, and Lollipop...

    Greg