Brian Long Consultancy & Training Services
Ltd.
August 2014 (updated to Delphi 10 Seattle in September 2016)
This is an update to a Delphi XE7 version of the same article. Note that this update is generally identical in content. The only reason for the update is to simplify the process of building the project, as Delphi 10 Seattle alleviates the need to use additional compiled Java code. The processes outlined in the original XE5 article and in the updated XE6 article and updated XE7 article will work perfectly well, but with Delphi 10 Seattle things are simplified to the point where we can do everything within Delphi (with a smattering of JNI) without need to recourse to Java.
Accompanying source files available for
Delphi XE5, Delphi XE6,
Delphi XE7,
Delphi XE8, Delphi 10 Seattle and
Delphi 10.1 Berlin are available.
Delphi Android apps can use Android APIs, both those already present in the RTL and others we can translate manually, in order to interact with parts of the Android system. This article will look at how we can make use of the NFC (Near Field Communications) API to access NFC tags in the same way as other Android NFC apps.
Through this article you should gain an insight into the techniques involved in building NFC apps and working with native Android NFC APIs, some of which are reasonably straightforward to employ and some of which involve metaphoricaly rolling up ones sleeves and getting ones hands dirty.
NFC tags have tag IDs so your app could just recognise an NFC tag and respond to it. However NFC tags can also have varying amounts of data written to them. This can include URLs, text or any other data. Your app can read this data and do things based upon what it finds.
An app that supports NFC tags can operate, broadly speaking, in one of two ways:
Either way the application setup will involve requiring permission to use the NFC sensor. In the project options dialog this is done in the Uses Permissions section:
If the use of NFC is crucial to the operation of your application you can also ensure this is known to the Android ecosystem. Indeed if you do this the Google Play online store will not list your app to devices that do not have NFC hardware. You do this by tweaking your Android manifest file, which as Delphi developers you cause to occur by editing the file AndroidManifest.template.xml that appears in the project directory when you first compile your mobile project.
Within the <manifest>
element you add this child element, which
will be a sibling to the <application>
element:
Before embarking on any NFC coding we need to have access to the Android NFC API. This is not provided as part of Delphi but that is not an insurmountable obstacle as there are various Android API import tools available. These import tools produce import units that follow the model used by those provided in Delphi's RTL. Delphi's native ARMv7 code communicates with the Java world using the Java Bridge (or JNI Bridge or Native Bridge as it is sometimes called) so these import units are often called Java Bridge units.
The ins and outs of using the Java Bridge interface to represent Java classes using
Delphi interfaces (one for the class methods and one for the interface methods,
bound together by the TJavaGenericImport
-based generic bridge class)
are a bit beyond the scope of this particular article, but I did go into it in
my CodeRage 8 session on using Android APIs from Delphi XE5:
One such Java API import tool, Java2Pas, was discussed on FMXExpress. It has been used by the person behind FMXExpress to import all APIs in the Android SDK. The results, for a whole range of Android SDK versions, can be found in a Github repository, which is a very useful (even if not particularly usable) resource.
As hinted by the parenthesised comment it turns out the NFC import units (and maybe others - I haven't yet had the need to check) need work to be suitable as input to the compiler. Indeed if you pull down, say the API level 19 import units, android.nfc.*.pas from https://github.com/FMXExpress/android-object-pascal-wrapper/tree/master/android-19 you'll find they won't compile for a whole heap of reasons including a gamut of circular unit references.
Anyway, not being one to be thwarted I took the interfaces and put them into units that were more in keeping with what the compiler was expecting, fixed some errors in the definitions and the resultant import units are included in the example downloads. I've coallesced what I needed from the 27 import units into two units called Androidapi.JNI.Nfc.pas and Androidapi.JNI.Nfc.Tech.pas.
Bear in mind that documentation for the various classes represented by these Java Bridge interface definitions can be found here and here on the Android API reference site. Additionally the Android documentation contains general NFC programming advice (targeted at Java programmers, but still useful background nevertheless), which can be found here.
Building an application that hooks into the NFC tag dispatch system is reasonably straightforward when you have the required API imports and have read the Android documentation on the matter.
In the downloadable source archive that accompanies this article there is an example app that uses the NFC tag dispatch system in the 1_TagDispatch subdirectory.
To tell Android to launch your application you must add additional intent filter definitions into the Android manifest for your application's main activity (the Android UI object that Delphi forms are rendered within). These are statements from your application saying that when a particular NFC action needs to be handled your activity can do the job. An intent is an Android system message represented as an object with varying amounts of associated data available through various methods.
When Android scans an NFC tag it parses the data on it to see if that data is of a known type, such as a URI or plain text. Then Android will locate a suitable activity to launch to handle the NFC tag. If more than one activity has registered an interest in the same tag capabilities then an activity chooser dialog is presented to the user.
In the case of an NFC tag containing NDEF (NFC Data Exchange
Format) records Android will parse the data and work out the MIME type
or URI. It will then launch an activity that has registered an intent filter for
ACTION_NDEF_DISCOVERED
(see Android documentation here) with the appropriate MIME type
or URI scheme as additional criteria.
If no activity registered an intent filter for the MIME or URI specifics on the
tag, or if the tag contains NDEF data not of a MIME/URI type, or if the NFC tag
is of a known tag technology but does not contain NDEF data then Android will launch
an activity registered for the
ACTION_TECH_DISCOVERED
intent (see Android documentation here).
Finally, if no activity handles either the ACTION_NDEF_DISCOVERED
or
ACTION_TECH_DISCOVERED
intents then Android looks for one that will
handle the ACTION_TAG_DISCOVERED
intent (see Android documentation here).
Having seen the general description let's now see some specifics of what we'd need to add into AndroidManifest.template.xml to register for some of these intents.
Any intent filter we add needs to be added as a child element of the existing
<activity>
element and as a sibling of the existing intent filter
for the LAUNCHER category of the MAIN action (this existing intent filter is how
Android knows which activity to list for an application in the apps list).
Let's say you want your application to specifically respond to an NFC tag that has
an NDEF record containing the URL of my web site:
http://blong.com. This can be done by crafting an intent filter or two for
ACTION_NDEF_DISCOVERED
, for example:
So here we are looking for any NDEF recognised data format where it is a http URI with either of the two host names that represent the site. If Android scans an NFC tag with either of those URLs then the app will be launched.
Similarly if you want your application to respond to an NFC tag containing plain text you define an intent filter specifying the plain text MIME type thus:
If you want your app to launch if either the specified URL or plain text is found on a scanned tag then you simply include both intent filters, and correspondingly you can add in any additional ones that make sense for your application as required.
If you want the URL response to be more general, so have the app launch for any URL, you would modify the intent filter like this:
You can use your imagination and try other combinations.
If you aren't particularly interested in any specific data or data type you can
instead look at the second of the two intents: ACTION_TECH_DISCOVERED
. There are a variety of independently
developed NFC tag technologies that may be present on a given tag and these are
all represented by Android classes as listed on this documentation page. This intent focuses on responding
to specified subsets of these technologies, which you list in an XML resource file
that you need to add to the deployment profile for your mobile FireMonkey project.
Each technology subset you want to respond to is set up in a <tech-list>
element in the resource file. You can see examples of how the resource file might
look on this Android documentation page. For example if you want
to respond to tags that support MifareUltralight, NfcA and Ndef technologies you
would need an XML file that looks like this:
Alternatively if you want your app to respond to any technology at all you could
set the resource file up with each technology specified alone in its own <tech-list>
:
To set up this resource file for a Delphi Android app you first save it as an XML
file. I recommend creating a res
directory in your project directory
and creating an xml
subdirectory within that. So for example your resource
file could be called res\xml\nfc_tech_discovered_filter.xml
, relative
to the project directory.
Once the file exists you can add it to the Delphi project deployment manager. In Delphi choose Project, Deployment to invoke the deployment manager. Hit the deployment manager toolbutton whose tooltip says: Add Files. Navigate to find your XML file and press Open to add your file to the deployed files list. Set up the deployment of this file by changing these column values in the deployment manager for your resource file:
res\xml\
This should leave the deployment manager ready to deploy your file:
With the resource file set up you can now add the required intent filter into the
Android manifest template file that refers to it via a <meta-data>
element:
The @xml
reference in the resource string tells Android to locate the
resource file in the xml
directory of the standard Android res
directory that Android apps typically include, compressed inside the final .apk
Android package file.
If no application has responded to the scanned NFC tag through ACTION_NDEF_DISCOVERED
or ACTION_TECH_DISCOVERED
then the final option of ACTION_TAG_DISCOVERED
comes into play. You can register an interest in this intent by changing the Android
manifest template thus:
Now we have the infrastructure nailed down we can look at what goes into the application to make it work when it gets automatically launched by Android when a suitable NFC tag is scanned.
The key to getting the app to work is to run some code when the application starts
up and becomes active, so the form's OnActivate
event would be helpful
here.
In the OnActivate
event handler we must
get the intent object that was passed to our activity by Android as that contains
a "parcelled" copy of the tag object for us to examine and make use of:
That takes us to a crucial point of being able to get to work on the tag object.
We'll come back to how to get the tag object out of the intent object and then pull stuff from an NFC tag object later.
I should be honest here and say that the tag dispatch system sample does demonstrate
a shortcoming here. If you have the app launched by Android for a tag then all is
well. However if another NFC tag is scanned while the app is in the foreground then
the app will crash, citing an error relating to there no being a looper for the
current thread. I haven't yet followed this up to see if there is a convenient
way of mitigating the problem. I did eventually resolve this. The Android manifest has an optional
launchMode
attribute
for the activity, which needed to be set to singleTask
.
The NFC foreground dispatch system is often the more interesting aspect of NFC tag reading as this is where an application "owns" the NFC sensor and can read whatever tags are presented to the device. However it is also the more cumbersome to implement as there is a little bit of a wrinkle in Delphi's support of Java classes.
In the downloadable source archive that accompanies this article there is an app that uses the NFC foreground dispatch system in the 2_ForegroundDispatch subdirectory. We'll look at what is involved in building such an application in this section.
The basics as far as Android itself is concerned are as follows. To take control
of the NFC sensor and enable the foreground dispatch system you call the default
NFC adapter object's enableForegroundDispatch()
method. To disable it
you call the corresponding disableForegroundDispatch()
method.
An application will be expected to enable the foreground dispatch system when it
comes to the foreground and disable it when it leaves the foreground. If you use
the IFMXApplicationEventService
FMX platform service to set up an application event handler,
this corresponds to when the event handler is triggered with the EnteredBackground
and BecameActive
values from the TApplicationEvent
enumerated type values.
When the foreground dispatch system is active Android will scan NFC tags and send
a suitable intent direct to your running activity by calling its onNewIntent()
method. The intent will contained a
"parcelled" copy of the tag object.
So that's how Android sees things - quite straightforward really. Fortunately when it comes to porting this over to Delphi it's not too much messier there.
However before we get onto the taxing parts (calling the NFC foreground dispatch system methods and receiving new intent objects) let's get some housekeeping sorted out. When the app starts it needs to be able to call NFC APIs and rather relies upon NFC being enabled, so let's check if that's the case. If it is not we can launch the system settings page for NFC to help the user out.
To get access to the NFC adapter we call the getDefaultAdapter()
class method of the NfcAdapter
class, passing in our activity context.
The adapter's isEnabled()
method tells us if NFC is switched on.
If not we construct an intent object passing in the activity action that shows the
settings and pass that intent to our activity's startActivity()
method (see here for more on this method) and leave the rest
to the Android system and the user. Additionally a toast message is displayed, making
use of my toast import unit, discussed elsewhere.
The Androidapi.JNI.GraphicsContentViewText.pas unit in the uses
clause
is part of the Delphi RTL and gives us access to JIntent
, the Java
Bridge representation of Android's Intent class. Androidapi.JNI.Nfc.pas is one the new pair
of units crafted to make use of NFC (as mentioned earlier).
Androidapi.JNI.Toast.pas is my import unit I made to import the Android toast message
functionality.
FireMonkey provides much functionality necessary for business applications needing to access data and display it to the user, but some common aspects of the Android OS are not 'wrapped up' in FireMonkey classes.
At this point you can call upon the various Android APIs that have been provided in the RTL, or craft or import new Java Bridge APIs if need be (as per the NFC API discussed earlier).
In cases where this isn't simply a case of calling into more APIs whose declarations need to declared with Java Bridge interfaces this poses a problem. Some examples of areas of the Android OS that are not wrapped up in Delphi 10 Seattle and are difficult to incorporate include:
In Delphi 10 Seattle these things and more require additional techniques, sometimes involving Java code to implement (though not for NFC!). If additional Java code is required to solve a certain problem, then some light hacking is needed to make use of it in a Delphi application, which tends to get in the IDE's way, so IDE building/installing of the application becomes interesting, and debugging becomes more challenging. These consequences tend to force you into managing a pair of synchronised projects in many cases (though not for NFC!):
Over and above this general Android integration isue there is also a problem with the level of support of Java APIs offered by Delphi's Java Bridge functionality. It turns out that Java Bridge can handle a good chunk of the Android API, but in the case of a method that takes a 2D array we become unstuck. Support exists for normal parameters and 1D arrays currently.
The NfcAdapter.enableForegroundDispatch()
method has
a signature containing 4 arguments:
PendingIntent
object that describes the intent to use
when an NFC tag is scannednull
/nil
to respond to all scanned NFC tags (for
ACTION_NDEF_DISCOVERED
and ACTION_NDEF_DISCOVERED
, coded
versions of what we saw earlier)null
/nil
for any NFC tag technology (for ACTION_TECH_DISCOVERED
, programatic
equivalent of the XML resource file we saw earlier)Notice that 4th parameter? It's a 2D array. This means that the Java Bridge is incapable of representing this API currently. So this means we are obliged to make the calls to it indirectly via lower level JNI calls.
Given the expectations of how the methods should be called it would be wise to plug
these calls into the IFMXApplicationEventService
handler.
To receive the intent that Android will send when the foreground dispatch system
scans an NFC tag we need to hook into the underlying activity's onNewIntent()
method. Fortunately the Android activity
present in our FireMonkey Android application has some new support in Delphi 10
Seattle for allowing us to pick up onNewIntent()
calls. New intents are now sent
into the RTL cross-platorm messaging system. We use message-receiving code like this to pick up new
intents and process them, This intent messaging is done via an additional (and seemingly
undocumented) use of the Android-specific TMessageReceivedNotification
message.
You can see that we use TMessageManager.DefaultManager
to subscribe to TMessageReceivedNotification
notifications
passing in HandleIntentMessage
as the handler before launching the
activity, where TMessageReceivedNotification
is actually a class inheriting
from a common TMessage
base notification message class. When the
new intent comes in the message notification passes all the information
to the HandleIntentMessage
method as a TMessage
and
we cast to type TMessageReceivedNotification
to access the intent
reference contained inside. This is then passed along to OnNewNfcIntent
.
This now leaves us in essentially the same position that we were in earlier with the tag dispatch system. We need to extract the tag and process it, which we'll look at shortly.
The application can now be compiled to a native Android library and then deployed into an Android application package (.apk file) using the Project, Compile NFC_Sample and Project, Deploy libNFC_Sample.so menu items respectively.
If you wish you can skip those two steps and have them done implicitly by choosing
the Run, Run (F9
) or Run, Run Without Debugging menu item (Ctrl+Shift+F9
).
This will install the app and launch it. If it is already installed then the app
will be re-installed, preserving any data that the app has already built up.
After all the preamble of how to set up the code for the two approaches to coding up an NFC app using either the tag dispatch system or the foreground dispatch system we can now look at the NFC tag object itself.
Both approaches ultimately end up with a tag object parcelled into an intent object that is delivered into your code. We need to pull out the tag object and inspect it and see what we can glean from it.
Here is the code from the second sample app's OnNewNfcIntent
method.
The first job is to ensure that the intent really represents a scanned NFC tag by
checking it against the 3 possible intent actions discussed above.
This foreground dispatch sample app starts with a prompt label suggesting the user scans a tag. If we now have an intent containing a scanned NFC tag object then the prompt label is cleared.
Now we get the tag from the intent as a Parcelable
object (Parcelable
is an
interface implemented by all parcelable objects, including NFC Tag
objects).
In Java we would simply cast the object back to a Tag
but it's a little
different using the Java Bridge interfaces in Delphi. To do the cast operation we
pass the JParcelable
interface to the TJTag
class's
Wrap
method.
So now we have an NFC Tag
object!
The code clears an information list box in preparation for some text to be written
there and then calls a helper routine, HandleNfcTag
, to get a dump
of what's in the tag object. HandleNfcTag
takes as arguments the tag
object and also a reference to a routine that will be passed various data pulled
from the tag object - in this case I'm passing in an anonymous method that adds in the new text into the information
list box. When the function is done it will return a string containing the NFC tag's
ID bytes turned into text, which are written onto another label.
Before we delve into the guts of the helper routine, here's the simple sample app showing information read from a scanned NFC tag:
The top line of text is the text rendering of the NFC tag ID bytes. The rest is
information pulled from the tag object. In this case the technologies supported
by my tag are MifareUltralight, NfcA and Ndef, each of which are represented by
Android classes, MifareUltralight
, NfcA
and Ndef
, which themselves are represented by
Java Bridge interface types in my Androidapi.JNI.Nfc.Tech.pas unit, JMifareUltralight
,
JNfcA
and JNdef
. You can see an overview of the various
tag technology classes in the Android documentation here.
Here's the helper routine laid bare:
The first thing it does is pull out the tag's ID, which comes out as a Java array
of bytes. A helper routine in the unit (JavaBytesToString
) iterates
over each byte calling IntToHex
on it to build up the textual ID.
The next task is to list out the tag technologies. The tag's getTechList()
method returns them as a Java array
of strings so a loop iterates across them adding Delphi string versions of them
into a TStringList
.
There are a variety of tag technologies as listed here and here. Both pages divide them into mandatory technologies, supported on all Android devices, and optional ones. However one of the pages lists seven mandatory tech types and the other lists just six, so the story is a little unclear. I haven't followed it up yet (it wasn't too important to me) so am currently unsure which is correct.
The sample code just pays attention to six mandatory tag technologies and checks for each one in turn. If the tag technology is supported by the tag then the tag is passed along to one of six helper routines designed to dump information for each of these tag tech types.
Five of the six dump routines are simple enough. They make use of the various methods exposed by the Java tag tech objects and add the results to the information string:
It's the Ndef
tag tech type dissection that contains all the
interesting bits. As the documentation page illustrates there are four types of standardised
NFC tags that support NDEF data. The methods in the class allow you to ascertain
certain standard information, such as the tag type (one of the four standardised
types), whether the tag is read-only or not, whether the tag can be made read-only,
the maximum NDEF message size and so on.
You can download the formal NDEF specification to get full details on the layout of data in an NDEF message from the NFC Forum or you can get summary information on the Android documentation site. For the sake of the sample we'll try and keep it simple.
On an NDEF tag is an NDEF message containing one or more NDEF records. We'll look through the records and see if they contain some information we can dump out.
You can get hold of an object that represents this NDEF message in the form of an
NdefMessage
object. You can either read a cached NDEF message that was read when the tag
was discovered, or read the current message, on the off-chance it may have
been updated. The sample code reads the cached message.
An
NdefMessage
object offers a getRecords()
method that returns a Java array
of
NdefRecord
objects.
Unfortunately when iterating over the record
array and trying to access each record therein we hit upon some crash bug whereby
the record objects aren't surfaced correctly for us. Fortunately we can work around
this problem by pulling the raw object ID from the array wrapper and manually wrapping
it into a JNdefRecord
Java Bridge object. Props to Daniel Magin for
spotting that issue and working out a nifty way round it!
Note: this issue no longer occurs in Delphi 10.1 Berlin and 10.2 Tokyo, however I cannot check for sure that the issue perists in Delphi 10 Seattle.
As you can see the record object offers a getTnf()
method to learn
its TNF or Type Name Format field. This is a 3 bit value with values available as
constants in the NdefRecord
class (see here). The TNF value dictates how you should parse the data
in the rest of the record. In the case of TNF_WELL_KNOWN
, which has a value of 1, the record
is one of a number of well known types and so the NFC Forum's Record Type Definitions
(RTD) kick in.
The RTD is a byte array and obtained through the record's getType()
method. You match it to one of the RTD
byte array fields offered by the NdefRecord
class to see which
known type it is, and thereby you can determine the NDEF message record payload
format.
The NFC Forum makes available the payload format for plain text, URI and smart poster.
The documentation there also confirms that the RTD byte arrays for text, URI and
smart poster are byte equivalents of T
($54), U
($55)
and Sp
($53, $70) respectively. The sample code caters for the first
two of these three but doesn't cover smart poster format.
I'll leave the code that decodes text and URI NDEF records out of this article and refer you to the sample code to see it instead. It would serve no specific benefit to the business of how to access NFC tags when it is really just a case of parsing byte arrays.
The key thing to take away from this article is that Delphi apps can make good use of NFC tags in either mode of standard NFC reading you choose.
Before leaving the subject we probably ought to also look at how to write to an NFC tag.
A common approach is to write an NDEF record to the tag, assuming the tag supports the NDEF technology. So the actual sample shipped in the downloads for this article really looks like this:
You can see text can be entered at the bottom of the form and the button can be used to write the text in an NDEF record in an NDEF message to an NDEF-compatible tag.
Incidentally, the sample app has an obvious shortcoming in that I haven't done anything to move the edit control up when the virtual keyboard appears on the screen, so when typing in text you can't see what you are typing into. However in this context that is an irrelevance.
The button has this code for an event handler:
It gets the activity's intent and pulls the NFC tag object from it. Note that in the earlier code we set the activity's intent to any new intent that we were delivered by the foreground dispatch system.
The tag then gets passed to a helper routine that looks like this:
This code tries to get an NDEF tag technology object for the scanned tag, which will only work if the tag is NDEF-compatible. Assuming we get the NDEF tag tech object we next open a connection to the tag in order to write to it, and also to later close the connection.
Assuming we establish a connection to the tag (which relies upon it still being
there) we can build up a message to write. The NdefMessage
class has
a constructor that takes a Java array of NdefRecord
objects, so we set up a suitable array of records.
In this sample app's case we write out 2 records - one is the text record generated
by another helper routine, EncodeText
, and one is a URI record generated
by a static method of the NdefRecord
class specifically for packaging
up URIs in the correct way. So as well as the text entered by the user the NFC tag
also gets a record containing my web site address.
Again, the details of encoding some text into a record aren't too important, but are available to read in the code sample. It just follows the information available in the NFC Forum text record type definition.
You can extend the functionality of your application by taking advantage of available Android functionality, including support for scanning NFC tags on devices that have an appropriate NFC sensor.
Writing code in an app that is auto-launched by Android when an NFC tag is detected is quite straightforward once you have the API imports available.
However having a regular app set up support for priority NFC tag scanning using the NFC foreground dispatch system is not quite so straightforward but quite manageable.
Go back to the top of this page