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?
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).
Related
Now that the PROVIDERS_CHANGED intent-filter can't be set in the Manifest, I'm doing it dynamically.
However, I need to register/unregister the dynamically coded Receiver in multiple places.
Due to this, I am creating a "Helper" Class that contains a static method with the Receiver code.
MY ISSUE: I can't seem to figure out how to get the Context in order to register/unregister receiver.
Here is my current code:
public class GpsReceiverHelper {
public static void gpsReceiverCode() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.location.PROVIDERS_CHANGED");
final BroadcastReceiver gpsReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() != null &&
intent.getAction().matches("android.location.PROVIDERS_CHANGED")) {
// RECEIVER CODE HERE
}
}
};
this.registerReceiver(gpsReceiver, intentFilter);
// THIS IS WHERE I CAN'T GET A CONTEXT (USING "this" OR OTHERWISE)
}
}
MY QUESTIONS:
(A) - How can I go about retrieving a context here?
(B) - Is creating a helper class with a public static method the right way to be doing this?
How can I go about retrieving a context here?
Pass it in as a parameter (public static void gpsReceiverCode(Context context)).
I need to register/unregister the dynamically coded Receiver in multiple places
When your receiver was in the manifest, it was "registered" in one place. Hence, when you do it dynamically, you can register it in one place (e.g., custom Application subclass).
Is creating a helper class with a public static method the right way to be doing this?
Probably not, insofar as you probably should not be needing to use this code in 2+ places.
I am implementing a service and have a wrapper class on top of this service wich locally bind to it. Now when I export the service as a jar and link to it from another application (this application instantiate the wrapper class).
When I run the application, I am getting the ClassCastException: android.os.BinderProxy cannot be cast to xxx$LocalBinder
caused by the local bind in the wrapper class:
service = ((LocalBinder) binder).getService();
The binder here is of type BinderProxy instead of localbinder and hence the crash.
The only way that this application works is when the package name of the app is the same as the service package name (I assume android thinks that the service is local).
private final IBinder localBinder = new LocalBinder();
public class LocalBinder extends Binder {
xxxService getService() {
return xxxService.this;
}
}
and
public IBinder onBind( Intent intent ) {
IBinder result = null;
result = localbinder;
return result;
}
Then in my wrapper class onServiceConnected:
public void onServiceConnected( ComponentName name, IBinder binder) {
xxxService = ((LocalBinder) binder).getService();
Finally my wrapper class constructor:
public xxxServiceManager( Context context ) throws Exception {
this.context = context;
xxxServiceManagerIntent = new Intent( "providerAPI" );
xxxServiceManagerIntent.setClassName( "com.yyy", "com.yyy.xxxService" );
context.startService( xxxServiceManagerIntent );
context.bindService( xxxServiceManagerIntent, serviceConnection, Context.BIND_AUTO_CREATE );
Then in the main application that uses this jar, if you set the package name
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yyy.provider" //the same as the service (which does not make sense)
everything work, but obviously I want to set another package name.
Is there any way how to redesign or make this work?
thanks!
see this
If your service is used only by the local application and does not need to work across processes, then you can implement your own Binder class that provides your client direct access to public methods in the service.
Note: This works only if the client and service are in the same
application and process, which is most common. For example, this would
work well for a music application that needs to bind an activity to
its own service that's playing music in the background.
So I'm following this example in Android developers:
http://developer.android.com/training/run-background-service/create-service.html
Creating a background service with IntentService.
Note that we define the class RSSPullService in the first code example:
public class RSSPullService extends IntentService {
#Override
protected void onHandleIntent(Intent workIntent) {
// Gets data from the incoming Intent
String dataString = workIntent.getDataString();
...
// Do work here, based on the contents of dataString
...
}
}
In the following page, Reporting Work Status:
http://developer.android.com/training/run-background-service/report-status.html
I'm confused, are we defining the same class again to get the status?
public final class Constants {
...
// Defines a custom Intent action
public static final String BROADCAST_ACTION =
"com.example.android.threadsample.BROADCAST";
...
// Defines the key for the status "extra" in an Intent
public static final String EXTENDED_DATA_STATUS =
"com.example.android.threadsample.STATUS";
...
}
public class RSSPullService extends IntentService {
...
/*
* Creates a new Intent containing a Uri object
* BROADCAST_ACTION is a custom Intent action
*/
Intent localIntent =
new Intent(Constants.BROADCAST_ACTION)
// Puts the status into the Intent
.putExtra(Constants.EXTENDED_DATA_STATUS, status);
// Broadcasts the Intent to receivers in this app.
LocalBroadcastManager.getInstance(this).sendBroadcast(localIntent);
...
}
Dont get confused,
both the classes are same
First one is to show how we create a service extending IntentService
Then they gave a example to send data to this IntentService
At last they gave example to shows how the same IntentService is returning result back.
Second code is just another example they changed the content of old intent service class
That's two separate examples, no need to define it twice, just use one definition. The code from first example (creating Intent Service) is just merged with code from Reporting Work example.
I've been trying workarounds/dealing with this problem for weeks and I can't seem to get around it any longer. I have an Android Application that needs to change the device's name before creating a Peer to Peer network.
Because the method is hidden in Android's SDK, I am using reflection. The method I am trying to reflect is located here at line 1305: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
Here is my code trying the reflection:
public class HostStagingActivity extends Activity
{
WifiP2pManager.Channel myChannel; //This channel is created and passed to system services in order for WIFI_P2P to work
WifiP2pManager myManager; //This manager is declared to get the required channel object
...
#Override
protected void onCreate(Bundle savedInstanceState)
{
myManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);//passed to the myReceiver listener class
myChannel = myManager.initialize(this, getMainLooper(), null);//passed to the myReceiver listener class
...
Method method1 = null;
try
{
method1 = myManager.getClass().getMethod("setDeviceName", new Class[] {WifiP2pManager.Channel.class, String.class, WifiP2pManager.ActionListener.class });
method1.invoke(myChannel, NETWORK_NAME, new WifiP2pManager.ActionListener()
{
public void onSuccess()
{
Toast.makeText(getApplicationContext(), "DeviceName Changed Successfully!", Toast.LENGTH_SHORT).show();
//Code for Success in changing name
}
public void onFailure(int reason)
{
Toast.makeText(getApplicationContext(), "DeviceName Change Returned Failure!", Toast.LENGTH_SHORT).show();
//Code to be done while name change Fails
}
});
}
However, this causes runtime error:
Caused by: java.lang.IllegalArgumentException: expected receiver of type android.net.wifi.p2p.WifiP2pManager, but got android.net.wifi.p2p.WifiP2pManager$Channel
at java.lang.reflect.Method.invokeNative(Native Method)
Why is the method expecting a WifiP2pManager object when I am clearly passing it a WifiP2pManager.Channel class in my code? Just for fun, when I pass it the arguements it expects, it claims the method was expecting three arguements and I only gave it two.
Can anybody with more reflection experience help me out?
When considering the case with android activity, the first method to work is its onCreate method..right?
Suppose i want to pass 2 parameters to android activity class say UserHome . For that am creating the constructor of activity class UserHome and accepting the params.
But when we are calling an activity we are not initializing the Activity class, we are just creating an intent of UserHome class.
Then how can we pass params to that activity from another activity without using intent.putExtra("keyName", "somevalue"); usage.
Experts please clarify how we can cover a situation like this.?
Not sure why you would not want to use the intent params. That is what they are there for. If you need to pass the same parameters from different places in your application, you could consider using a static constructor that builds your intent request for you.
For example:
/**
* Sample activity for passing parameters through a static constructor
* #author Chase Colburn
*/
public class ParameterizedActivity extends Activity {
private static final String INTENT_KEY_PARAM_A = "ParamA";
private static final String INTENT_KEY_PARAM_B = "ParamB";
/**
* Static constructor for starting an activity with supplied parameters
* #param context
* #param paramA
* #param paramB
*/
public static void startActivity(Context context, String paramA, String paramB) {
// Build extras with passed in parameters
Bundle extras = new Bundle();
extras.putString(INTENT_KEY_PARAM_A, paramA);
extras.putString(INTENT_KEY_PARAM_B, paramB);
// Create and start intent for this activity
Intent intent = new Intent(context, ParameterizedActivity.class);
intent.putExtras(extras);
context.startActivity(intent);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Extract parameters
Bundle extras = getIntent().getExtras();
String paramA = extras.getString(INTENT_KEY_PARAM_A);
String paramB = extras.getString(INTENT_KEY_PARAM_B);
// Proceed as normal...
}
}
You can then launch your activity by calling:
ParameterizedActivity.startActivity(this, "First Parameter", "Second Parameter");
I can see one situation where you'd be unable to use the standard method of passing the parameters via the Intent: When you're creating an activity that will be launched by another app (say, the edit activity of a Tasker plugin) and, therefore, do not have control over the Intent that will launch your activity.
It's possible to create an Activity that accepts parameters in its constructor. The trick to using it, though, is not to use it directly, but to use a derived class with a default constructor that calls super() with the appropriate arguments, as such:
class BaseActivity extends Activity
{
public BaseActivity(String param1, int param2)
{
// Do something with param1 and param2.
}
// Many more lines of awesomeness.
}
class DerivedActivity extends BaseActivity
{
public DerivedActivity()
{
super("param1", 42);
}
}
Naturally, if you need to generate the parameters to pass to BaseActivity(), you can simply replace the hard-coded values with function calls.
We can pass the value from parent activity to child activity using the bundled collection and shared preference.
1. Shared Preference
2. Bundle Collection
Passing data or parameter to another Activity Android
But you also can create very well a constructor of UserHome.
public class MainActivity extends Activity {
UserHome userHome = new UserHome(param1,param2);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
userHome.yourMethod();
}}
Why do you think that is not possible to initialize a contructor?..MainActivity is a class like any other, just that extends Activity, but also keeps the properties of a class, so that can have, constructors, methods, members.