DEVELOPERS BLOG

Porting BlackBerry Java NFC Applications to BlackBerry 10 – Part 1: Reading NFC Tags

CASCADES / 09.11.13 / mdwrim

find milan

Our buddy Rob Williams included some words of wisdom on porting BlackBerry OS Java applications that read or write NFC tags in a blog post that embraced both these aspects of NFC and Bluetooth, a few months back.

John Murray and I would like to circle back on the subject of porting BlackBerry Java NFC applications to BlackBerry 10 and cover the subject in more detail and across all the primary capabilities and use cases of NFC.

To that end, we’ll be publishing a series of six blog posts about porting NFC apps from BlackBerry Java to BlackBerry 10 native applications over the coming weeks, starting with this one. The full list of posts in this series will be:

  1. Reading NFC Tags
  2. Writing NFC Tags
  3. Peer to Peer Mode
  4. Reading NFC Contactless Cards
  5. NFC Virtual Tag and Card Emulation
  6. NFC Card Emulation

So let’s take it away and discuss reading NFC Tags and how to turn BlackBerry Java apps that do this into BlackBerry 10 native apps.

Key issues for the Java developer:

  1. Registration

The application must implement the NDEFMessageListener interface and register it with ReaderWriterManager.addNDEFMessageListener(…)

  1. Invocation

NDEFMessageListener’s on NDEFMessageDetected method is called directly if the app is already running when tag whose content matches the type registered for is detected.

The application must handle being restarted when a tag is presented as a special case in the code.

  1. Decoding tag content

We decode tag content with classes in net.rim.device.api.io.nfc.ndef.rtd or direct from byte[] payload

Tags contain NDEFMessage and NDEFRecord objects

The BlackBerry 10 Native approach:

1. Registration

Applications must register for particular tag content with an entry in the bar-descriptor.xml file as shown here:

        <invoke-target id="com.example.TouchTicTacTen.ndef">                     <require-source-permissions>invoke_across_perimeters</require-source-permissions>                     <type>APPLICATION</type>                     <filter>                         <!-- The action(s) we support-->                         <action>bb.action.OPEN</action>                         <mime-type>application/vnd.rim.nfc.ndef</mime-type>                          <!-- ndef:// means that we want to register for ALL NDEF messages,                             in most cases we want to be more specific; we can filter these                             messages further with the following format ndef://TNF/RECORD_TYPE                             (e.g. ndef://1/Sp for tnf: Well Known, record type: smart posters).                             for URI record types the record type needs to be URL encoded                             (e.g. ndef://3/http%3A%2F%2F for tnf: Uri, record type: http://).                             for external record types the colon (:) needs to be replaced with a                             forward slash (/)                             (e.g. ndef://4/rim.com/bbm for tnf: External, record type: rim.com:bbm).                              TNF VALUES:                             0 : Empty Tag                             1 : Well Known                             2 : Media (MIME)                             3 : Uri                             4 : External -->                             <property var="uris" value="ndef://4/com.blackberry.nfc.sample/G1"/>                     </filter>                 </invoke-target>

2. Invocation

When a tag containing content that matches the details registered for in the bar-descriptor.xml file is detected, the invocation framework determines which application to dispatch the tag content to and if it is not already running, launches it. Developers should use the InvokeManager class and signals/slots to receive a callback when this happens and extract the tag payload from the InvokeRequest object. Here’s an example:

MyApp::MyApp(Application *app) {   _invokeManager = new bb::system::InvokeManager();   QObject::connect(_invokeManager,                    SIGNAL(invoked(const bb::system::InvokeRequest&)),                                   this,                    SLOT(receivedInvokeRequest(const bb::system::InvokeRequest&))); .... } void MyApp::receivedInvokeRequest(const bb::system::InvokeRequest& request) {                 QByteArray data = request.data();                 if (request.mimeType().compare("application/vnd.rim.nfc.ndef") == 0) {                                 qDebug("XXXX launched because an NFC tag has been presented"); // next we decode the content of the InvokeRequest                 } }

3. Decoding tag content

There are two APIs to choose from:

3.1 Qt

BlackBerry 10’s APIs includes the QtMobilitySubset API. This contains some useful methods for handling tag data in the NDEF format. For example:

QtMobilitySubset::QNdefMessage ndefMessage = QtMobilitySubset::QNdefMessage::fromByteArray(data); QList<QtMobilitySubset::QNdefRecord>::const_iterator ndefRecord;  for ( ndefRecord = ndefMessage.begin(); ndefRecord != ndefMessage.end();        ndefRecord++) {    if (ndefRecord->typeNameFormat() == QtMobilitySubset::QNdefRecord::ExternalRtd)   {     if (QString(ndefRecord->type()).compare("my.rim.com:myrecordtype") == 0 ) { // ......     }    } }

3.2 BPS

The BlackBerry Platform Services (BPS) APIs include methods intended for transforming byte [] format tag payloads into NDEF structures. Code fragments:

nfc_get_ndef_message(target, ndefMsgIndex, &ndefMessage); for (int ndefMsgIndex = 0; ndefMsgIndex < ndefMsgCount; ++ndefMsgIndex) {   unsigned int ndefRecordCount = 0;   CHECK(nfc_get_ndef_record_count(ndefMessage, &ndefRecordCount));   for (unsigned int ndefRecordIndex = 0;     ndefRecordIndex < ndefRecordCount; ++ndefRecordIndex) {     nfc_ndef_record_t *ndefRecord;     CHECK(nfc_get_ndef_record(ndefMessage, ndefRecordIndex, &ndefRecord));     uchar_t* ndefRecordPayloadData;     size_t ndefRecordPayloadLength;     CHECK(nfc_get_ndef_record_payload(ndefRecord, &ndefRecordPayloadData, &ndefRecordPayloadLength));     tnf_type_t ndefRecordTnf;    CHECK(nfc_get_ndef_record_tnf(ndefRecord, &ndefRecordTnf));     char *ndefRecordNdefType;     CHECK(nfc_get_ndef_record_type(ndefRecord, &ndefRecordNdefType));     char *ndefRecordIdentifier;     CHECK(nfc_get_ndef_record_id(ndefRecord, &ndefRecordIdentifier));     QString ndefRecordPayloadAsHex = QString::fromAscii(     reinterpret_cast<const char *>(ndefRecordPayloadData),                 ndefRecordPayloadLength).toAscii().toHex();     QByteArray payLoadData = QByteArray::fromRawData(                 reinterpret_cast<const char *>(ndefRecordPayloadData),                 ndefRecordPayloadLength);     if (strcmp(ndefRecordNdefType, Settings::NfcRtdSmartPoster) == 0) {       char *utf_title;       char *found_lang;       char *uri;       nfc_ndef_rtd_encoding_t rtd_encoding;       rtd_encoding = UTF_8;        CHECK(nfc_get_sp_title(ndefRecord, Settings::LANG_EN, &utf_title, &found_lang, &rtd_encoding, true));       CHECK(nfc_get_sp_uri(ndefRecord, &uri));       free(utf_title);       free(uri); } else if (strcmp(ndefRecordNdefType, Settings::NfcRtdUri) == 0) { // etc

That’s it! Reading NFC tags using the BlackBerry 10 APIs is very easy, unarguably easier than it was using BlackBerry Java. Porting this aspect of your application should take very little time and effort indeed.

Resources: Helpful knowledge base articles can be found here.

Sample Code: Tag reading via Invocation Framework and then decoding using the BPS APIs is demonstrated by the NfcTool application can be found here.

Tag reading via Invocation Framework and then decoding using the Qt APIs is demonstrated by the NfcRaceTimeWay application can be found here.

Have questions or comments? Reach out to us on Twitter at @mdwrim, @jcmrim and @robbieDubya.

About mdwrim