Android NFC passing single parameter when starting application

4.9k views Asked by At

I would like to start an application using an NFC tag. I got that part working using an android application record (AAR) as described in Start Android application from NFC-tag with extra data or by using NDEF_DISCOVERED / TECH_DISCOVERED intent filters. But how do I pass data from the NFC tag (e.g. some text) to my activity upon starting it through the NFC event?

I've read through NFC Basics, but as far as I understand that it seems to want to implement a mechanism for reading the tag, when I really do not want to re-read the tag once the app is opened by the tag, but instead I just want the data passed in at the same time.

Moreover, these mechanisms seem to allow the app to read the tag after it has been started by the tag. In other words, I am worried that if someone hits the tag later when the app is already opened that tag will be read again (which is what I do not want).

Second, how do I create such NDEF message?

1

There are 1 answers

2
Michael Roland On BEST ANSWER

Android will automatically read the NDEF message of an NFC tag and process it in order to

  • start registered activities based on the first NDEF record, and
  • start apps based on Android Application Records (AAR) anywhere in the NDEF message.

In order to get your activity started and have Android pass the pre-read NDEF message, you could use the NDEF_DISCOVERED intent filter:

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <category android:name="android.intent.category.DEFAULT" />
    <data android:scheme="vnd.android.nfc"
        android:host="ext"
        android:pathPrefix="/example.com:mycustomtype"/>
</intent-filter>

Then from within your activity, you could process that NDEF message:

public void onResume() {
    super.onResume();
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
        NdefMessage[] msgs = null;
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMsgs != null) {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; ++i) {
                msgs[i] = (NdefMessage)rawMsgs[i];
            }
        }

        if ((msgs != null) && (msgs.length > 0)) {
            NdefRecord[] records = msgs[0].getRecords();
            NdefRecord firstRecord = records[0];
            byte[] payloadData = firstRecord.getPayload();

            // do something with the payload (data passed through your NDEF record)
            // or process remaining NDEF message

        }
    }
}

Note that onResume() is run whenever your activity becomes the foreground activity. Hence, it might be run multiple times for the same tag. THerefore, you could either use another life-cycle method or take some precautions that you do not parse the message multiple times.

If you want to drop all further NFC events, once your activity is open, you could follow the approach that I described in response to Android app enable NFC only for one Activity. Hence, you would register for the foreground dispatch (which gives your activity priority in receiving NFC events, and you can then simply drop those events.

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

public void onPause() {
    super.onPause();
    NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
    nfcAdapter.disableForegroundDispatch(this);
}

public void onNewIntent(Intent intent) {
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) {
        // drop NFC events
    }
}

Finally, to create the NDEF message for your NFC tag, you would do something like this:

byte[] payload = ...  // generate your data payload
NdefMessage msg = new NdefMessage(
    NdefRecord.createExternal("example.com", "mycustomtype", payload)
)

If you want to make sure that only your app is started by this tag (or if not installed Play Store is opened for your app), you could also add an AAR:

NdefMessage msg = new NdefMessage(
    NdefRecord.createExternal("example.com", "mycustomtype", payload),
    NdefRecord.createApplicationRecord("com.example.your.app.package")
)