Use beacon’s NFC in an app

The new Proximity Beacons come with a built-in NFC tag, which enables apps to trigger beacon events not only on proximity, but also on the very explicit gesture of touching a beacon. It’s almost as if we’ve added a new API method to the physical world! Read on to learn how to make use of it.

What’s ahead (aka Table of Contents)

Prerequisites

  • 1 or more Proximity Beacon (min. hardware revision “G”) or Location Beacon (min. hardware revision “F3.3”)
  • 1 x device with Android & NFC
  • 1 x computer with Android Studio

“NFC Stamps” example

To make it easy to follow this tutorial, we’ve prepared an example app which you can use to quickly try NFC in beacons out. You’ll find it in our Android SDK repo on GitHub:

https://github.com/Estimote/Android-SDK/tree/master/Examples/NFCStamps

Set up Android Application Record

Let’s start by setting the Android Application Record (“AAR” in short), broadcast by the beacon over NFC, to your app’s package name. This will make sure that your app, and only your app, will start when touching a smartphone to the beacon. (And as a bonus: if the app is not installed, it’ll open its page in Google Play Store instead.)

The easiest way to do that is with the Estimote Android app (version 1.7.6 or later)—simply connect to your beacon, go to the Android Application Record option, and set your app’s package name.

Tip: If you’re following this tutorial with the “NFC Stamps” example, the package name is: com.estimote.nfcstamps

Once saved, touching your smartphone to the beacon should already open the app.

Opening specific activity

Your app already pops up, but what if you want it to open a specific activity in response to an NFC event? You can see that in the “NFC Stamps” example—opening the app as usual shows you the stamps you’ve collected (ProfileActivity), but if it opens via NFC, it navigates straight to the “new stamp” screen instead (PointsActivity).

That’s where intent filters come in. In the AndroidManifest.xml file, find the activity that you want to trigger in response to an NFC event, and add an <intent-filter> to it:

<activity
    android:name="com.estimote.nfcstamps.presentation.points.PointsActivity"
    android:launchMode="singleTask"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data
            android:host="ext"
            android:pathPrefix="/estimote.com:id"
            android:scheme="vnd.android.nfc" />
    </intent-filter>
</activity>

Long story short, the <intent-filter> above says that if there’s an NFC event triggered by any Estimote Beacon, the app should open the PointsActivity.

Important: If the package name set in the Android Application Record doesn’t match that of the activity, Android will instead open the app specified in the AAR. Make sure the AAR package name matches that of the app!

Estimote NDEF records

In the last section, to set up the intent filter to only consider NFC events coming from Estimote Beacons, we used one of two of our pre-defined NDEF (NFC Data Exchange Format) records. All NFC-equipped Estimote Beacons always include these two records in their NDEF messages, in addition to the Android Application Record, and/or any custom records you defined.

Beacon identifier

The first record contains the identifier of the beacon that triggered the event. It has a TNF (Type Name Format) Field set to 0x04 (i.e., “External Record”, hence the android:host="ext" in the <intent-filter> above), RTD (Record Type Definition) set to estimote.com:id (hence the android:pathPrefix="/estimote.com:id"), and its payload set to the beacon’s identifier.

Beacon’s connectable MAC address

The other record contains the connectable MAC address of the beacon that triggered the event. It has a TNF Field set to 0x04 (i.e., “External Record”), RTD set to estimote.com:mac, and the payload is of course the MAC address itself.

Accessing NDEF record’s payload

Say you want to trigger different actions depending on which beacon the user has touched their smartphone to. For that, you’ll need to access the payload of the estimote.com:id record, to read the beacon’s identifier.

In the activity you’ve set to trigger in response to an NFC event, add:

@Override
protected void onResume() {
    super.onResume();
    // ...
    if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        if (rawMsgs != null) {
            for (int i = 0; i < rawMsgs.length; i++) {
                NdefMessage msg = (NdefMessage) rawMsgs[i];
                DeviceId beaconId = findBeaconId(msg);
                if (beaconId != null) { /* do something */ }
            }
        }
    }
}

private static DeviceId findBeaconId(NdefMessage msg) {
    NdefRecord[] records = msg.getRecords();
    for (NdefRecord record : records) {
        if (record.getTnf() == NdefRecord.TNF_EXTERNAL_TYPE) {
            String type = new String(record.getType(), Charset.forName("ascii"));
            if ("estimote.com:id".equals(type)) {
                return DeviceId.fromBytes(record.getPayload());
            }
        }
    }
    return null;
}

Further reading