How do I register in manifest an *inner* MEDIA_BUTTON BroadcastReceiver?
Asked Answered
P

2

3

I managed to get my headset buttons get recognized by my app when pressed, but one of the buttons needs to call a method that's in MyCustomActivity. The problem is onReceive's 1st parameter is a Context that cannot be cast to Activity and so I am forced to implement my BroadcastReceiver as an inner class inside MyCustomActivity.

So far so good but how do I register this inner MediaButtonEventReceiver in the manifest?

For the independent class, this was simple:

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

What is the trick/syntax to do the same for MyCustomActivity's mReceiver?

  private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context ctx, Intent intent) {
         // ...
        }
  }
Preventer answered 25/2, 2013 at 1:16 Comment(0)
T
5

You don't, if it's meant to be part of the Activity, you register it dynamically:

BroadcastReceiver receiver;

@Override
protected void onCreate (Bundle b)
{
  super.onCreate (b);
  IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);    
  filter.setPriority(10000);  
  receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context ctx, Intent intent) {
      // ...
    }
  };
  registerReceiver (receiver, filter);
}

Then don't forget to unregister in onPause() (to avoid leaking).

@Override
protected void onPause()
{ 
  try{
    unregisterReceiver (receiver);
  }
  catch (IllegalStateException e)
  {
    e.printStackTrace();
  }
  super.onPause();
}

This dynamic registration does mean however, that if your Activity isn't in the foreground, the button won't work. You can try unregistering in onDestroy() instead, but the surest way to avoid leaking is onPause().

Alternatively, to make the button respond no matter what, consider making a Service, and having that register your receiver.

Tisman answered 25/2, 2013 at 1:22 Comment(14)
Thanks! I thought that I tried this but seeing new BroadcastReceiver() instantiated again in onCreate(), I am going to try this one more time and report back how it worked. +1 for now.Preventer
Strange. I tried this approach but it doesn't work for some reason. What am I missing?Preventer
@Preventer are you leaving the Activity? Remember we're unregistering in onPause() to avoid leaking. Also not working as in compiles but doesn't do anything when ran? I recomment making a Toast in onReceive() to check if the Receiver is being called at all.Tisman
It compiles & builds fine. It's the receiver that doesn't get called. I'm not leaving the activity.Preventer
@Preventer maybe what CommonsWare is referring to is something like this question. AudioManager#registerMediaButtonEventReceiver() And I don't really see what Intent.ACTION_MEDIA_BUTTON does differently on account of that being the static variable that holds the String "android.intent.action.MEDIA_BUTTON". Still worth a try though.Tisman
It's the filter.setPriority(10000); that eventually did the trick. Interesting. Plus the ACTION_MEDIA_BUTTON instead of MEDIA_BUTTON, of course.Preventer
@Preventer hmm, I wonder why this is an ordered broadcast, i'd expect it to be unordered. But nice that you found that out :D Hopefully this will help more people.Tisman
@Tisman my guess why it is ordered is so that say answering/ending a phone call takes precedence over starting/pausing music.Shuping
BTW, that above posted code works great in Android 2.x but doesn't work in Android 4.1 (Jelly Bean). Android Fragmentation Uggrrr...Preventer
@Preventer This is interesting. I guess you can make the receiver start the Activity with with the singleTop flag so that the new Intent gets routed to onNewIntent() and update the UI There.Tisman
@Tisman Due to the fact that your wonderful solution doesn't work in 4.1, I am still searching for a solution (or workaround). Do you have additional suggestions? Thanks.Preventer
@Preventer might be wasteful, but have this Receiver make a custom broadcast for another BroadCastReceiver implemented by your Activity.Tisman
It's not wasteful if this is the only way to solve this problem. :) Why did Google choose to make our lives so miserable by eliminating goodies that were available in Android 2.x?Preventer
@Preventer You don't by any chance have your updated 4.1 code readily avaialble as a project of some sort? I'm on a custom ROM and the media button refuses to work, so I need someone's working implementation to check :)Tisman
H
1

So far so good but how do I register this inner MediaButtonEventReceiver in the manifest?

You can't. You can register it dynamically by calling registerReceiver() on the activity, though.

Hierolatry answered 25/2, 2013 at 1:21 Comment(6)
Ouch! I thought that would be the case, so I tried the tip provided here but for some reason it didn't work. Must I new BroadcastReceiver() in onCreate() or can I do it when initializing a private data member mReceiver?Preventer
@an00b: Either way works. Usually I set up a private data member for the receiver.Hierolatry
private data member for the receiver that's what I have too, but none of the 2 approaches work. I may be missing something simple but I don't know what it is. The key presses do not get directed to my inner receiver as in the standalone one.Preventer
@an00b: The media button may be special. Check out AudioManager -- I seem to recall there are some things you need to set up on there to get control from a running app. Normally, A--C's approach works fine.Hierolatry
I suspected so. Perhaps I should be using a receiver class other than BroadcastReceiver? Is there a "MediaButtonEventReceiver"? Also, this answer says "use ACTION_MEDIA_BUTTON instead of MEDIA_BUTTON".Preventer
@an00b: Not that I am aware of.Hierolatry

© 2022 - 2024 — McMap. All rights reserved.