11
votes

Im new to Firemonkey and Android and i dont know if i have the wrong approach. I want to have a app running and read a NFC-Tag.

Is there a way to use the NFC Reader in android devices with firemonkey?

With a part of the NFCAdapter from FMXExpress (http://www.fmxexpress.com/full-android-sdk-interface-files-in-object-pascal-for-firemonkey/) i can determine if the device have a NFC Reader and if it is enabled. But to use all functions, i had to define all interfaces manually and solve all circular references. I dont think this can lead to a prober solution.

Im facing the solution to build my own JavaClass to communicate with nfc adapter like it is described in this blog post: http://blong.com/Articles/DelphiXE5AndroidActivityResult/ActivityResult.htm#Building

1
If you're still stuck on this, @deterministicFail, I've written up how to read and write NFC tags in Delphi Android apps for XE5, XE6 and XE7. You can find the post with links to all three articles here: blog.blong.com/2014/09/delphi-and-nfc-on-android.html Enjoy!blong

1 Answers

22
votes

Yes! You are taking the right approach.

To make Delphi and FireMonkey work you can use the following template as it is the right way to achieve what you have in mind:

Interface:

/ JNI NFC import demo
// Note - REQUIRES - PROJECT OPTIONS - USES PERMISSIONS - NFC

interface

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.GraphicsContentViewText,
  FMX.Helpers.Android,
  SysUtils,
  Classes;

type

NFC Adapter:

  /////////////////////////// NfcAdapter /////////////////////////////////
  JNfcManager = interface;
  JNfcAdapter = interface;

  JNfcAdapterClass = interface(JObjectClass)
  ['{634258AC-7931-4E38-97E6-48DBF688A288}']
    {Property methods}
    function _ACTION_TAG_DISCOVERED: JString; cdecl;
    function _EXTRA_ID: JString; cdecl;
    function _EXTRA_NDEF_MESSAGES: JString; cdecl;
    function _EXTRA_TAG: JString; cdecl;
    {Properties}
    property ACTION_TAG_DISCOVERED: JString read _ACTION_TAG_DISCOVERED;
    property EXTRA_ID: JString read _EXTRA_ID;
    property EXTRA_NDEF_MESSAGES: JString read _EXTRA_NDEF_MESSAGES;
    property EXTRA_TAG: JString read _EXTRA_TAG;
  end;

  [JavaSignature('android/nfc/NfcAdapter')]
  JNfcAdapter = interface(JObject)
  ['{364D8F3F-23AE-4C28-A261-E30C0893B24C}']
    //Return true if this NFC Adapter has any features enabled
    function isEnabled: Boolean; cdecl;
  end;

  TJNfcAdapter = class(TJavaGenericImport<JNfcAdapterClass, JNfcAdapter>) end;

NfcManager:

  /////////////////////////// NfcManager /////////////////////////////////

  JNfcManagerClass = interface(JObjectClass)
  ['{812481E1-F491-47D2-AC1F-4C5AB509532B}']
  end;

  [JavaSignature('android/nfc/NfcManager')]
  JNfcManager = interface(JObject)
  ['{04B707EC-966A-4E4F-85DC-F003B7C9ACE3}']
    {Methods}
    function getDefaultAdapter: JNfcAdapter; cdecl;
  end;

  TJNfcManager = class(TJavaGenericImport<JNfcManagerClass, JNfcManager>) end;

function HasNfc: Boolean;
function IsNfcEnabled: Boolean;

implementation

function GetNfcManager: JNfcManager;
var
  ConnectivityServiceNative: JObject;
begin
  ConnectivityServiceNative := SharedActivityContext.getSystemService(TJContext.JavaClass.NFC_SERVICE);
  if not Assigned(ConnectivityServiceNative) then
    raise Exception.Create('Could not locate Nfc Service');
  Result := TJNfcManager.Wrap((ConnectivityServiceNative as ILocalObject).GetObjectID);
  if not Assigned(Result) then
    raise Exception.Create('Could not access Nfc Manager');
end;

function HasNfc: Boolean;
var
  NfcManager: JNfcManager;
  NfcAdapter: JNfcAdapter;
begin
  NfcManager := GetNfcManager;
  NfcAdapter := NfcManager.getDefaultAdapter;
  Result := Assigned(NfcAdapter);
end;

function IsNfcEnabled: Boolean;
var
  NfcManager: JNfcManager;
  NfcAdapter: JNfcAdapter;
begin
  NfcManager := GetNfcManager;
  NfcAdapter := NfcManager.getDefaultAdapter;
  Result := Assigned(NfcAdapter)and NfcAdapter.isEnabled;
end;

end.

{code}

usage
Memo1.Lines.Add('Nfc Enabled: '+BoolToStr(IsNfcEnabled, True));

In case you play around with this sample code, I am pretty sure that it will work!

I guess, you are forced to get the best of everything, such as using an API that is totally cross and multi-platform, that will let you have exactly the same code running on Android and iOS without any change of your App's front and back-end. Yep, it's true that some tools are currently under development targeting to solve real multi-platform embedded development, which should achieve fully integration possibly in the near future. Unfortunately, the reality is that at current stages of mobile platforms development, you only can go for such multi-platform tools if you are implementing more basic Apps that won't depend on more specific resources, such as NFC, Geofencing, etc. Of course, if you are not in a production environment, then eventually you may have all the time to go playing and hacking around for fun. But supposing that not to be the case, then keep focus on putting things together to work fast, as delivery deadline is usually tight.

That said, alternatively the fast and shortest way to achieve NFC-tag communication is simply by doing it with the Android NFC APIs resource, as it was introduced since API level 9 - Android Gingerbread.

enter image description here

NFC transfers can occur between two NFC-enabled devices, or between a device and an NFC “tag”. Tags can range from passive tags that transmit a URL when scanned to complex systems as those used in NFC payment solutions, such as Google Wallet.

enter image description here

In order to read, write, or broadcast NFC messages, your application requires the NFC manifest permission:

<uses-permission android:name=”android.permission.NFC” />

When an Android device is used to scan an NFC tag, the system will decode the incoming payload using its own tag dispatch system, which analyzes the tag, categorizes the data, and uses an Intent to launch an application to receive the data.

Here is a snapshot code showing how to register an Activity that will respond only to NFC tags:

<activity android:name=”.BlogViewer”>
    <intent-filter>
        <action android:name=”android.nfc.action.NDEF_DISCOVERED”/>
        <category android:name=”android.intent.category.DEFAULT”/>
        <data android:scheme=”http”android:host=”blog.example.com”/>
    </intent-filter>
</activity>

The NfcAdapter.EXTRA_TAG extra includes a raw Tag object that represents the scanned tag. The NfcAdapter.EXTRA_TNDEF_MESSAGES extra contains an array of NDEF Messages:

String action = getIntent().getAction();

if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
    Parcelable[] messages =
    intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    for (int i = 0; i < messages.length; i++) {
        NdefMessage message = (NdefMessage)messages[i];
        NdefRecord[] records = message.getRecords();
        for (int j = 0; j < records.length; j++) {
            NdefRecord record = records[j];
            // TODO Process the individual records.
        }
    }
}

enter image description here

The potential and possibilities of NFC applications is huge, and the demand for such technology tends to grow significantly:

enter image description here

If you can use your smartphone as a payment method, then paying for things like public transport and parking meters could be as simple as swiping your phone. You could even tap a newspaper terminal on your daily commute to download the latest issue to your device.