13

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 using a MyCustomActivity's inner class won't work in Android 4.1 unless it is static (which has the same problem of inability to access MyCustomActivity's method.

So the only option left for me (in order to support both 2.x and 4.1) is to pass the activity as a parameter to RemoteControlReceiver.

But how do I do that, when the only way to instantiate it is via:

private ComponentName mRemoteControlReceiver = new ComponentName(this, RemoteControlReceiver.class);

Which doesn't accept any additional parameters?

Any idea how to work around this limitation?

Note: If I try to define RemoteControlReceiver as having a constructor with a parameter, I receive the following exception:

E/AndroidRuntime(2836): java.lang.RuntimeException: Unable to instantiate receiver com.example.RemoteControlReceiver: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor

Caused by:
E/AndroidRuntime(2836): Caused by: java.lang.InstantiationException: can't instantiate class com.example.RemoteControlReceiver; no empty constructor
E/AndroidRuntime(2836):     at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime(2836):     at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime(2836):     at android.app.ActivityThread.handleReceiver(ActivityThread.java:2205)

So it is clear that this new registerMediaButtonEventReceiver requirement (introduced in Android 4.1) expects an empty constructor.

Is there no way to work around this?

For example, is there a way to get a reference to the actual RemoteControlReceiver object (instantiated indirectly via mAudioManager.registerMediaButtonEventReceiver())? So that I can use an accessor to set a data-member of RemoteControlReceiver after it has been instantiated?

Community
  • 1
  • 1
an00b
  • 11,338
  • 13
  • 64
  • 101
  • 1
    you can create and register the BroadcastReceier in onResume/onCreate of the Activity and unregister inside onPause/onStop. In this way the current activity can take care of its life time and the receiver should be able to communicate to its container (Activity). – Sudar Nimalan Feb 27 '13 at 00:30
  • 2
    @SudarNimalan I have already tried this. This [only works in Android 2.x](http://stackoverflow.com/questions/15058743/how-do-i-register-in-manifest-an-inner-media-button-broadcastreciver#comment21174031_15058783). It doesn't work in 4.1. I need to be able to somehow let `RemoteControlReceiver` (not BroadcastReceiver!) know about MyCustomActivity. Thanks. – an00b Feb 27 '13 at 00:40
  • This is a tough one but here is an idea: Can you check the [Extra](http://stackoverflow.com/a/14383023/418055) passed with the Intent onReceive? Or use this [GlobalVariable trick](http://stackoverflow.com/a/6980006/418055)? – Android Eve Feb 27 '13 at 02:21
  • see this answer may be this help you. http://stackoverflow.com/questions/15059886/updating-textview-in-an-activity-with-local-service/15060082#15060082 – Jignesh Ansodariya Feb 27 '13 at 04:55

1 Answers1

7

registerMediaButtonEventReceiver requires the BroadcastReceiver to be declared in the application manifest. This means that the receiver must be a standalone class, meaning it knows nothing about your current activity or service.

In order to get this message to your activity or service, you have a number of options:

  • Use a static global for the activity or service so the receiver can forward the message to it. This is generally not a good idea as it leads to leaks and isn't very adaptable when you want to change the code later. Statics are generally to be avoided.

  • Re-broadcast the message to a specific class, which happens to be an inner class of the activity or service you want to invoke. E.g. in the BroadcastReceiver for registerMediaButtonEventReceiver:

    // Standalone class, declared in the manifest
    public class ButtonReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(final Context context, final Intent intent) {
            Intent intent = new Intent();
            intent.setAction("com.foo.ACTION");
    
            // Rebroadcasts to your own receiver. 
            // This receiver is not exported; it'll only be received if the receiver is currently registered.
            context.sendBroadcast(intent); 
        }
    }
    

And in your activity:

    class MyActivity extends Activity {
        private BroadcastReceiver myReceiver = new BroadcastReceiver() {
            @Override
             public void onReceive(final Context context, final Intent intent) {
                MyActivity.this.onMessageReceived();
             }
        }
        @Override
        protected void onResume() {
            registerReceiver(myReceiver, new IntentFilter("com.foo.ACTION"));
        }

        @Override
        protected void onPause() {
            unregisterReceiver(myReceiver);
        }

        private void onMessageReceived() {
        }
    }
  • Similar to the above method, it doesn't necessarily have to be a broadcast, it could be an Intent passed to the activity, depending on your use case. To do this instead of using sendBroadcast, you'd use startActivity (or startService if you're using a service).
Sofi Software LLC
  • 3,879
  • 1
  • 36
  • 34