I know you can simulate NFC tags simply by creating an intent and starting and activity with it. From my understanding and testing this only works, if you add intent-filters to your manifest.
I want to simulate tags and dispatch them to an activity/fragment via foreground dispatch only, so starting the activity with intent-filters in the manifest is not an option for me.
The structure of my program looks like this: Activity -> several Fragments, one of them is interested in NFC tags via foreground dispatching. The nfc-fragment has the necessary code for pendingIntent and dispatch-enabling and -disabling. The activity implements the onNewIntent() method, which invokes further handling of the intent via a method in the nfc-fragment, if the nfc-fragment is active.
The program works fine, but I need to test the behaviour with automated tests.
I've already tried using
final Intent intent = new Intent(NfcAdapter.ACTION_TAG_DISCOVERED);
intent.putExtra(NfcAdapter.EXTRA_ID, "1234567890".getBytes());
solo.getCurrentActivity().startActivity(intent);
but this just gives me an ActivityNotFoundException. Currently I retrieve the nfc-fragment and call the method to handle the intent manually from inside the test, but this gives me
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
since the method involves updating Views and whatnot. It works somehow, since I just need to switch to a different activity or fragment and then go back to get the view updates, but I'd like to know if there is a better and cleaner way of doing this.
I appreciate your help, let me know if you need more information.
I assume that solo.getCurrentActivity()
refers to the activity that should receive the NFC intent, otherwise you have to adapt the activity class and context to refer to the right values:
Class activityCls = solo.getCurrentActivity().getClass();
Context packageContext = solo.getCurrentActivity();
Then you create the pending intent (better yet, you could use the PendingIntent that was passed to the enableForegroundDispatch() method):
PendingIntent pendingIntent = PendingIntent.getActivity(
packageContext,
0,
new Intent(packageContext, activityCls).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
0);
Set up the parameters of the NFC intent:
String intentAction = NfcAdapter.ACTION_TAG_DISCOVERED;
Tag tag = ...;
byte[] tagId = ...;
NdefMessage ndefMessage = ...;
Prepare the NFC intent:
Intent intent = new Intent();
intent.setAction(intentAction);
intent.putExtra(NfcAdapter.EXTRA_TAG, tag);
intent.putExtra(NfcAdapter.EXTRA_ID, tagId);
if (ndefMessage != null) {
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, new NdefMessage[] { ndefMessage });
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intentAction)) {
Uri uri = message.getRecords()[0].toUri();
String mime = message.getRecords()[0].toMimeType();
if (uri != null) {
intent.setData(uri);
} else {
intent.setType(mime);
}
}
}
Send the pending intent using the parametrization set-up above:
pendingIntent.send(packageContext, Activity.RESULT_OK, intent);