Android: Handle headset buttons events and Send information to MainActivity
Asked Answered
H

1

7

Here's my effort to make a working code to handle a headset button event the best way. I read the Android developer guide, but it is obviously wrong because they ask to start listening registering a class name.

am.registerMediaButtonEventReceiver(RemoteControlReceiver); // Wrong

So I check out other examples to correct the code. For example many secret suggestions have been published in this question, I also tried other code such as this, and another solution with MediaSession, and cleaning the unneeded I wrote this code:

I implemented the class RemoteControlReceiver. Apparently there is no need for a static inner class, in fact, see this comment:

public class RemoteControlReceiver extends BroadcastReceiver {

        public RemoteControlReceiver() {
            super();
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "EVENT!!", Toast.LENGTH_SHORT).show();
            if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
                KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
                if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
                    Toast.makeText(context, "EVENT!!", Toast.LENGTH_SHORT).show();

                }
            }
        }
    }

Then I registered the intent inside the MainActivity onCreate(){...

    AudioManager am = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE);
    ComponentName mReceiverComponent = new ComponentName(this, RemoteControlReceiver.class);
    am.registerMediaButtonEventReceiver(mReceiverComponent);

The registerMediaButtonEventReceiver is deprecated by the way...

Inside the manifest I recorder the filter, after the activity tag:

<activity>
...
</activity>

<receiver android:name=".RemoteControlReceiver" android:enabled="true">
    <intent-filter android:priority="2147483647">
        <action android:name="android.intent.action.MEDIA_BUTTON" />
    </intent-filter>
</receiver>

Note: with a static inner class would be, e.g., ".MainActivity$RemoteControlReceiver".

I am working on

compileSdkVersion 24
buildToolsVersion "24.0.0"
...
minSdkVersion 21
targetSdkVersion 24

Here my questions:

  • Why the registerMediaButtonEventReceiver is deprecated? Seems all this paradigm is wrong nowadays, but I found no information on how to handle these class of problems on the Android Developer Portal.
  • Which way may I interact with the MainActivity? My purpose is to perform an action on the MainActivity when some headset operation has been performed.
Hype answered 26/9, 2016 at 4:44 Comment(2)
Your manifest says MainActivity$MediaButtonReceiver but your class is called RemoteControlReceiver. Which is it?Taffrail
Thank you @Taffrail for niticing that. It was just a copy paste typo though, I have implemented many receivers during all the experiments. Now I corrected it, I confir the setup is as described.Hype
T
8

API 21 changed the entire media app APIs, now centering entirely around MediaSession. Instead of registering a BroadcastReceiver (as was needed prior to API 18) or a PendingIntent (via registerMediaButtonEventReceiver(PendingIntent)), you can receive callbacks directly in the MediaSession.Callback.

You can set up a MediaSession via the following code:

MediaSession.Callback callback = new MediaSession.Callback() {
  @Override
  public void onPlay() {
    // Handle the play button
  }
};
MediaSession mediaSession = new MediaSession(context,
  TAG); // Debugging tag, any string
mediaSession.setFlags(
  MediaSession.FLAG_HANDLES_MEDIA_BUTTONS |
  MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(callback);

// Set up what actions you support and the state of your player
mediaSession.setState(
  new PlaybackState.Builder()
    .setActions(PlaybackState.ACTION_PLAY |
                PlaybackState.ACTION_PAUSE |
                PlaybackState.ACTION_PLAY_PAUSE);
    .setState(PlaybackState.STATE_PLAYING,
      0, // playback position in milliseconds
      1.0); // playback speed

// Call this when you start playback after receiving audio focus
mediaSession.setActive(true);

If you only want to handle media buttons while your activity is visible, you can just have your MediaSession handled by the Activity itself (this would allow your Callback to just be a variable in your Activity).

The Best practices in media playback talk from I/O 2016 goes through all of the details and other APIs required to build a great media app, although note that it uses MediaSessionCompat and the other Support Library classes as detailed in the Media playback and the Support Library blog post.

Taffrail answered 28/9, 2016 at 1:48 Comment(5)
That's it @ianhanniballake, I wanted to find information about the new best practices. All the attached references will also be useful. Thank you!Hype
@ianhanniballake, what if i just want to listen to buttons events, without playing any media. Is there a way to do that?Waterspout
i'm also looking for a way to listen to button presses without actually playing media. (only while app visible).Suavity
tried the sample code from Ian and it works now. seems that i had wrong order of mediaSession setup. (setting state before setActive seems to be crucial)Suavity
Hi! On my phone triple press on headset started to be responded to as double + single in an 3rd party app (app not changed when behavior changed). Do you know if triple is single event or 3 separate events? I want to understand better before maybe making a bug report. TIAHandgrip

© 2022 - 2024 — McMap. All rights reserved.