Wednesday, 10 September 2014

Delphi and NFC on Android

NFC is a neat technology.

If you’ve got an NFC sensor on your phone or tablet then there is a reasonable chance you’ve played around with apps like Trigger, which perform various actions on your device when you wave it over NFC tags in stickers or on key fobs etc. after a bit of setup.

So, for example, you could have an NFC sticker by the front door that has been coded by Trigger to turn off Wi-Fi and Bluetooth. With a variety of of NFC tags around and about you can set up your phone how you need it when you need it by a simple wave, avoiding a whole lot of fiddling about in the device settings.
Anyway, that’s one application of NFC tags. There are, of course, plenty more.

Indeed there have been a good number of questions in various forums asking how to get programmable NFC support in Delphi. Of course NFC is not currently “wrapped up” by Delphi’s RTL so up until now it’s been in the hands of the “creative”.

NFC tag scanning support in Android falls into 2 areas:

  1. You install an app that advertises that it can respond to some NFC tag content. When the device is waved against such a tag the app can be automatically launched. If more than 1 app is registered Android will initially launch a chooser dialog to allow the user to pick the app they require.
    Actually this support is easy enough to do in Delphi so long as you can locate a suitable import unit or two containing the required Android APIs for NFC.
  2. You run an app that permits the user to scan NFC tags while it is in the foreground and it directly responds to any scanned tags.
    This seems to be more commonly required in commercial applications and alas is much more difficult to achieve with Delphi. It falls into the category of problem that requires some Java smarts and some command prompt hackery to achieve.
    Bummer.

Anyway, I was recently looking into writing some NFC support, given the frequency of questions being asked. Part way through my research I discovered that Daniel Magin was simultaneously trying to do the same thing, primarily for a talk at Delphi Tage.

I helped Daniel out with the Java callback and command-line building trickery – that seems to be an area where I can readily add value to Android low level chicanery – and he helped me out overcoming a nasty crash bug with Java arrays of NFC data records not being surfaced correctly by the Delphi RTL.

In the end we both had some sample apps that permit NFC scanning and also, just as an added bonus, NFC writing!

You can find my solutions written up in quite some detail here:


Daniel’s efforts will also be made available very soon.

Enjoy coding up your NFC apps, everyone!

Update - Dec '15


A number of people had tried to use the code on versions of Android more recent than was current when I worked all this and hit problems with the code crashing. This was down to some inappropriate use of JNI constructs where the problem didn't originally show up but did show up as later versions of Android got more picky about things. I've remedied this issue in 2 ways.

1) I've updated the samples and code snippet in the article (it's OnNewIntentNative that has changed, FYI, in its usage of the intent JNI value).
2) However I've also devised a different sample app (for XE7, XE8 and 10 Seattle) that does much the same thing but doesn't really use explicit JNI. Instead it uses a listener interface implemented in Delphi and called from Java. The app has been slightly extended to allow the NFC support to be toggle on and off via a checkbox.

Updated samples:

Update – Sep ‘16

While catching up on what’s new in recent releases of Delphi I realised a change had been snuck into Delphi 10 Seattle that makes the whole NFC exercise much more straightforward now in Delphi. No more Java required! No more activity subclassing! No more debugging challenges!

It turns out that the key activity callback that NFC foreground dispatch relies upon (Activity.onNewIntent) is now hookable from Delphi code (with the right know-how) and so all the Java shenanigans can be put behind us (at least in the context of NFC access).

I have updated the article to show the more direct means of accessing NFC from Delphi 10 Seattle and later and provided corresponding updated samples for Delphi 10 Seattle and Delphi 10.1 Berlin.

33 comments:

  1. Not found - 404

    URL requested (/Articles/Articles/DelphiXE6NFC/NFC.htm) not found

    ReplyDelete
  2. Go it:

    http://blong.com/Articles/DelphiXE7NFC/NFC.htm

    ReplyDelete
    Replies
    1. Thanks for posting the correction Robert. I fixed the post now.

      Delete
  3. i prepared parallel with brian the nfc stuff. my article is now also online:

    http://www.danielmagin.de/blog/index.php/2014/09/nfc-android-application-with-delphi-xe6-and-xe7/

    ReplyDelete
  4. Your comment on problems with the Delphi RTL not being able to surface Java array of NFC records seem to gel with my G+ comment nearly a year ago regarding issues with Java arrays.

    ReplyDelete
    Replies
    1. Chewy, quite possibly, though the details of a conversation from that long ago escape me now :o)

      Delete
  5. You might be interested in this request of mine:
    https://quality.embarcadero.com/browse/RSP-9715

    ReplyDelete
    Replies
    1. If I could get onto the web site I'd take a look :-/

      Delete
  6. Hello Brian, I'm developing a app based on your code, i made some modifications to read and write Mifare Classic cards. When i test in your app everything works and i can read and write the cards, but when i run in my app the app fail to start when i change the AndroidManifest. Can you give me some ligth?!! thanks in advance,

    ReplyDelete
    Replies
    1. Hello Laurence. First thing to do is run the Android SDK log utility by running monitor.bat or ddms.bat (they are in the Android SDK directory tree) and see what the log says about the failure to start. It should give a clue.

      Delete
  7. Hello Brian, Your code works fine, but if I try to install the app on a device with no NFC support, the Application crashes. I'm using the Nfc as a login so in case of no NFC support, the user could enter something like a password. WHat can I do to prevent the crash in that case?

    ReplyDelete
    Replies
    1. Hi Gianluca. It's a good question that I hadn't considered. I'd need to look at such a scenario to see *why* it is failing in that manner, but I assume I have a lack of defensive coding somewhere along the line, making too many assumptions. I'm unlikely to be able to check this out in the immediate short-term future, but hopefully that gives you a clue where to look. If I get a handle on the problem, I'll update the blog post.

      Delete
  8. Dear Brian,
    I use Delphi XE7 and I were bult your sample NFC project to my porject. Whewn I want to run my project on an NFC phone it wokrs fine. But if I want to run a phone what doesn't has NFC my app has stopped after the splash screen.

    ReplyDelete
    Replies
    1. Hello Arpad, see my reply to Gianluca just above

      Delete
  9. Hello Brian, I succesfully installed the example on foreground dispatch on my nexus 7 (2013) with android 5.0.2. Compiled with XE7 Update1.
    If i scan a tag when the program is active the program crashes.
    I receive the following message in the debug log: "W/InputEventReceiver(15107): attempted to finish an input event but the input event receiver has already been disposed".

    ReplyDelete
    Replies
    1. Hello. I haven't got Android 5 on any of my devices yet, but I know it impacts the JNI bridge stuff notably, hence the (currently beta) fix for FMX and Lollipop (http://cc.embarcadero.com/item/30110).
      I see this download now has source/headers in so I can potentially see what changes have been made to support Lollipop.
      In short, I'm *guessing* your crash may be more to do with running code on a Lollipop device than not having followed the steps, and my code doesn't take Lollipop into account.
      It would be a useful exercise to see if installing that beta fix (on a VM snapshot or something) helped improve the situation.

      Delete
  10. I use XE7 and Mobile Android 5.0.
    Use Delphi XE7 NFC support Code
    Touch NFC, The program will close
    I would like to ask how to deal with this issue.
    Thank you!

    ReplyDelete
    Replies
    1. So you're saying with Android 5, as soon as you engage with an NFC tag the app closes unexpectedly? Have you installed Embo's Lollipop fix? http://cc.embarcadero.com/item/30110
      Definitely try that, If that doesn't help, you'll need to use DDMS or Monitor (from Android SDK) to look at logcat messages to get an inkling as to what the crash relates to, I'd imagine.

      Delete
    2. Thank you for your reply.

      http://cc.embarcadero.com/item/30110 I have installed,but Touch NFC, The program will close.

      My Mobile Sony Z2 Android 5.0.2. XE7 up1
      Use you Delphi XE7 NFC support
      I did not modify the program
      The program will close.
      I would like to try to identify the problem,but I have insufficient capacity.

      Delete
    3. If I open the program will crash with NFC sensors
      Touch NFC, The program will close

      But if I can run directly sensing NFC induction program and will open
      But only once
      When the program opens after they die

      Delete
    4. I think it might be related with NFC android Beam

      Delete
    5. I did hear people saying that on Android 5.x, code that references fully qualified Android classes needs to now use a '/' instead of a '.' to separate the different parts of the class name.
      Maybe that might help?
      I haven't seen the issue, so it's tricky for me.
      From the comments it doesn't look like you've examined the logcat messages in monitor/DDMS. They may be instructive.

      Delete
    6. I have 2 devices running greater than Android 5.X. On the initial read, it works great. but then requires the user to exit the application in order to get any further reads. Somehow every subsequent read (I think) is reloading the application and trying to access NFC. I don't know if that helps.

      Delete
    7. Rather late I know, but I sorted out that issue. Probably irrelevant for many of you, but the issue turned out to be the way I was using JNI code in the sample, which in later versions of Android started failing, but was fine in earlier versions.
      I've updated the samples and the snippet of code in the articles. If you pull down the current sample and do a compare with the original version (or just check out OnNewIntentNative in the article) you pulled down, you should see the subtle change in the use of the JNIObject parameter.
      There's also an additional example that doesn't really use JNI at all - it uses a listener interface, which is a neat way round the problem.
      Apologies for not getting to this sooner.

      Delete
  11. Brian, I am fairly certain now that it is a Android 5.x issue. I've run the same set of test APKs on both styles 4.4 and 5.X and 5.X has an issue getting the second intent in the same application or getting an intent in an already running app.

    ReplyDelete
    Replies
    1. Duly noted, Mary. I'll re-run the sample on my 5.0.2 device, though I am reasonably confident it was at 5.0.2 the last time I tried.
      I'll see what happens though.

      Delete
    2. Rather late I know, but I sorted out that issue. Probably irrelevant for many of you, but the issue turned out to be the way I was using JNI code in the sample, which in later versions of Android started failing, but was fine in earlier versions.
      I've updated the samples and the snippet of code in the articles. If you pull down the current sample and do a compare with the original version (or just check out OnNewIntentNative in the article) you pulled down, you should see the subtle change in the use of the JNIObject parameter.
      There's also an additional example that doesn't really use JNI at all - it uses a listener interface, which is a neat way round the problem.
      Apologies for not getting to this sooner.

      Delete
  12. Same problem, android 5 (samsung s5 stock rom)

    ReplyDelete
    Replies
    1. Rather late I know, but I sorted out that issue. Probably irrelevant for many of you, but the issue turned out to be the way I was using JNI code in the sample, which in later versions of Android started failing, but was fine in earlier versions.
      I've updated the samples and the snippet of code in the articles. If you pull down the current sample and do a compare with the original version (or just check out OnNewIntentNative in the article) you pulled down, you should see the subtle change in the use of the JNIObject parameter.
      There's also an additional example that doesn't really use JNI at all - it uses a listener interface, which is a neat way round the problem.
      Apologies for not getting to this sooner.

      Delete
  13. I brian, si there some information to implement nfc on IOS devices?

    ReplyDelete
    Replies
    1. Hi Carlos, unfortunately I haven't done much on iOS lately, so I don't have any information on surfacing NFC support. It's one of the many things I hope to get onto as and when the current workload dies down, but I am unaware of when this will be.

      Delete