How to test BroadcastReceiver's setResultData() with Robolectric - android

My BroadcastReceiver receives Intent.ACTION_NEW_OUTGOING_CALL. It needs to call this.setResultData(null) so that the number is not subsequently dialled, as described here.
This works fine.
How can I test this with Robolectric?
This is what I have:
// Create the intent
Intent myIntent = new Intent("Intent.ACTION_NEW_OUTGOING_CALL");
myIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, "123");
// Find my BroadcastReceiver
List<BroadcastReceiver> receivers = ShadowApplication.getInstance().getReceiversForIntent(myIntent);
MyReceiver receiver = (MyReceiver)receivers.get(0);
// Invoke it - I understand this is the correct way to do it in a Robolectric test
Context context = ShadowApplication.geInstance().getApplicationContext();
receiver.onReceive(context, myIntent);
My Broadcast receiver is launched, but when it calls setResultData() an exception is raised: java.lang.IllegalStateException: Call while result is not pending. This happens due to the state of the underlying BroadcastReceiver because setResultData()->checkSynchronousHint() finds that mPendingResult==null.
mPendingResult is only ever set non-null by setPendingResult(), which I cannot call (even using reflection).
What is the proper way to test BroadcastReceiver which needs to call setResultData() please?

I've tried to reproduce your issue and was not able.
Take a look to my test https://github.com/emartynov/robolectic-experiments/blob/master/robolectric-3.0-test/src/test/java/com/bijdorpstudio/myapplication/IncomingCallReceiverTest.java
However, I was not able to get BroadcastReceiver to be picked up by Robolectric from AndroidManifest.xml. Added by hand in tests.
And I think it is OK approach. I would add some xml test for correct BroadcastReceiver tagging in AndroidManifest.xml. Testing that android is correctly picking it up will be still my lowest priority task

Related

Android SDK : Broadcastreceiver Intentservice and configuration changes

I am using a broadcastreceiver to listen for an intentservice which is set off via an onclick method. This is all fine but if the screen is rotated, I have had to put an unregister in the onPause (I have also tried this in onDestory) method and then in the onResume I re-register the broadcastreceiver.
My problem is, during the time of the broadcastreceiver being unregistered/registered the intentservice can send its broadcast back to say its done and then this means that the broadcastreceiver is still sitting there waiting for something that has already happened.
I have tried to use a sleep in the intentservice before the broadcast but this is by no means full proof as if the device is rotated again there is a timing issue.
I start and register the intent service and broadcastreceiver from the onClick as follows :
Intent intentMyIntentService = new Intent(myclass.this,myservice.class);
startService(intentMyIntentService);
mybroadcastreciver = new br();
IntentFilter intentFilter = new IntentFilter(myservice.ACTION_ServiceE);
intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(mybroadcastreciver, intentFilter);
I the br method is the broadcastreceiver which as its onReceive method and I handle the configuration change with the onSaveInstanceState method and then check in the onCreate to see if the Bundle != null to then retrieve the Boolean which are set depending whether the intentservice is complete using the broadcast that it sends to the receiver.
This does all work if the screen is not rotated. However I know there are other ways to handle screen rotation but I am writing an app to work from API 8 to 17 .
Thanks
Tim
One way is to maintain the same BroadcastReceiver object across screen orientation change:
1) Return the BroadcastReceiver object in Activity.onRetainNonConfigurationInstance().
2) Fetch it in Activity.onCreate() using (BroadCastReceiver) getLastNonConfigurationInstance(). It returns null if not saved.
3) In onClick(), only create the BroadcastReceiver object if it's not obtained in Activity.onCreate().

Android - Intent declaration crashing my app on boot

I have an app with :
an activity class that allows the user to set multiple alarms.
a service class to manage those alarms in the background.
a receiver class to do certain work when the alarm is called.
Everything works fine.
Now I want to automatically start the service when the phone boots up. The onBootReceiver is received but the app crashes (NPE) when this line is reached in my service class:
Intent intent = new Intent (MainActivity.getContext(),AReceiver.class);
I can't use this instead of MainActivity.getContext() either.
Any ideas of what may be causing this?
Thanks :)
From your code sample, it looks like the MainActivity class is not being initialized when being passed into the Intent. This means that the getContext() method will return a null value, and thats where your error is.
You need to use getContext() or getApplicationContext() from a initialized object. If this proves impossible, you could do something like this.

Persisting BroadcastReceiver reference

What I want to achieve is to give user a button saying 'Start broadcast receiving' and another one saying 'Stop broadcast receiving'.
I'm registering BroadcastReceiver for "android.provider.Telephony.SMS_RECEIVED" intent ('Start broadcast receiving' functionality):
incomingSmsReceiver = new IncomingSmsReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("android.provider.Telephony.SMS_RECEIVED");
getApplicationContext().registerReceiver(incomingSmsReceiver, filter);
Then I'm using unregisterReceiver() for 'Stop broadcast receiving':
getApplicationContext().unregisterReceiver(incomingSmsReceiver);
As you can see it's using the same reference (private static BroadcastReceiver incomingSmsReceiver;).
The problem is:
This works fine as long as my app's process is not terminated. When user click 'Start receiving broadcast' and after that my app is been killed by Android I'm loosing incomingSmsReceiver reference (when I run my app next time it's set to null by default). There's no way for user to stop receiving broadcast as the reference is lost.
How to persist this reference? And how to make it possible to call getApplicationContext().unregisterReceiver(incomingSmsReceiver); after recreating app's process by Android?
I've found better solution for such problem: Enable and disable a Broadcast Receiver (CommonsWare's answer).
The solution is to register BroadcastReceiver in AndroidManifest file. Then to use PackageManager.setComponentEnabledSetting(...) to enable / disable this component.
AFAIK, you don't need to hold on to the exact same BroadcastReciever reference. Create a new reference in the exact same way in which you would create one normally and pass it to unregisterService.

Correct way of handling (pending) intents in AppWidgets

I have a question regarding AppWidget intent handling. I have a widget which is clickable, and on click I want to send an intent to the AppWidgetProvider itself for further processing.
The problem: I receive the intents initially in onReceive(), but after a while (not sure what causes it), onReceive() is no longer called.
I have the following code, all in MyWidgetProvider extends AppWidgetProvider.
a) register for receiving broadcasts:
in onEnabled(...):
context.getApplicationContext().registerReceiver(this, new IntentFilter(MY_ACTION));
b) set intent to be fired on click:
in onUpdate(...)
Intent intent= new Intent(MY_ACTION);
PendingIntent pendingIntent= PendingIntent.getBroadcast(context, 0/*notusedanyway*/, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.widget_root, pendingIntent);
c) react to event and do something:
in onReceive(...)
if (MY_ACTION.equals(intent.getAction())
doSomething();
When I deploy + add a widget, it works fine. However, after a while - not sure what exactly causes the problem, but a phone call, for example, seems to affect it - I no longer get any notifications in onReceive().
I am completely stumped why this is the case. Can someone point out to me the correct way of doing this?
Thanks!
Tom
You should use a BroadcastReceiver registered in your AndroidManifest.xml file. When you register it in onEnable it is tied to the process. Whenever Android kills your process (for example, when a phone call is received) then your receiver no longer exists and (as you observed) no longer works.

Why is there no test instrumentation for BroadcastReceiver?

Maybe I'm missing something. I want to write test cases for a BroadcastReceiver; specifically, it is for receiving the BOOT_COMPLETED event and setting an alarm for another receiver to handle later; it doesn't seem to be setting it properly, but the point is that I have no obvious way to test it. I can't exactly attach a debugger and wait for BOOT_COMPLETED, and I can't send a fake BOOT_COMPLETED broadcast.
Why are there instrumentation classes for Activity, Service, and Provider, but not BroadcastReceiver? Any advice for testing this?
There is nothing magical about the life cycle for the BroadcastReceiver. It's enough to test it with an AndroidTestCase. In a test case, instantiate your BroadcastReceiver, create whatever Intent you want to send and call onReceive using the Context available from AndroidTestCase or some mock Context.
E.g.
public class TestMyBroadcastReceiver extends AndroidTestCase {
public void testReceive() {
MyBroadcastReceiver r = new MyBroadcastReceiver();
Intent i = new Intent("MY_ACTION");
// TODO put extras
r.onReceive(getContext(), i);
// TODO query application state to verify results
}
}
For most cases I agree completely with https://stackoverflow.com/a/5181010/527016
There are however cases when extending AndroidTestCase is not suitable (and can cause surprises). In particular, if you are doing more complex integration testing and want to test your BroadcastReceiver with an actual Intent sent by the system. The main reason is that the onReceive method in the broadcast receiver runs on the main application thread while the tests in AndroidTestCase run in another thread. This can cause test-related threading issues in code that was not intended to run on multiple threads.
The solution to this is to subclass your test from InstrumentationTestCase instead and use the #UiThreadTest annotation to make the tests run on the same thread as the onReceive method.
For more info (and an example) see: http://olafurhelgason.blogspot.com/2012/12/threading-and-android-integration.html

Categories

Resources