androidandroid-intentnfcndefandroid-beam

Launch app via NFC and send a NDEF message back when started


I'm developing an Android app which interacts with other devices using NFC. This interaction consists of 2 steps basically:

  1. When the device receives a specific URI by NFC from other device, the app is launched.
  2. When the app starts, it sends a NDEF message back to the other device.

For the first step, I have added the following lines to the AndroidManifest.xml file. That way, the MainActivity will be launched when the device receives a URI of type myprotocol:something:

<intent-filter>
     <action android:name="android.nfc.action.NDEF_DISCOVERED" />
     <category android:name="android.intent.category.DEFAULT" />
     <data android:scheme="myprotocol" />
 </intent-filter>

For the second step, my MainActivity class implements CreateNdefMessageCallback and OnNdefPushCompleteCallback. The code looks like following:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
    adapter.setNdefPushMessageCallback(this, this);
    adapter.setOnNdefPushCompleteCallback(this, this);
}

@Override
public NdefMessage createNdefMessage(NfcEvent event) {
    NdefRecord uriRecord = NdefRecord.createUri("protocol:something");
    NdefMessage message = new NdefMessage(new NdefRecord[] { uriRecord });
    return message;
}

@Override
public void onNdefPushComplete(NfcEvent event) {

}

So, now the problem:

These two parts work fine independently, but not when I add both to the app.

That is, if I add the first part, the app is correctly launched when receiving the NDEF message. Also, if I only add the second part, if I tap my device to other device while running the app, I see Touch to beam interface, and the NDEF message is sent.

The problem is, that if I add both, when tapping to the other device, the app is launched, but the Touch to beam interface is never shown. If I separate the devices and tap again, the MainActivity is relaunched, but I never get to see the option to send the message.

How could I achieve the desired scenario?


Solution

  • A one-tap approach is not possible using Beam on two Android devices (note that with other devices, particularly if one is Android and one is a dedicated NFC reader or a device where you can control the NFC functionality on a low level or a device that emulates an NFC tag).

    However, a two-tap approach is possible between two Android devices with only little modifications to your existing scenario. You simply need to add a foreground dispatch that intercepts your incoming NDEF message and consequently prevents Android from restarting your activity:

    @Override
    public void onResume() {
        super.onResume();
        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(this);
        PendingIntent pi = PendingIntent.getActivity(
                this,
                0,
                new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP),
                0);
        adapter.enableForegroundDispatch(this, pi, null, null);
    }
    

    EDIT

    A more general approach for the two-tap scenario would be to send the NDEF message from device A to device B on the first tap. This NDEF message starts the app on device B. Immediately after sending the NDEF message, device A stop sending the message. When the app on device B is active, it registeres its own NDEF message for Beam. Then, in the second tap, the Beam UI will be shown on device B and clicking the Beam screen will send the response NDEF message to device A.

    Note that device A must stop sending its initial NDEF message. Otherwise, the app on device B will receive a new NDEF message and, consequently, won't open the Beam UI.