Search code examples
javascriptandroidnfcintentfilterappcelerator-titanium

NFC app based on Titanium is not triggered by tag


I have a problem with Appcelerator Titanium's ti.nfc module.

I added the module and followed this guide:

http://vizteck.com/test/blog/nfc-data-communication-android-using-titanium

I also followed the Appcelerator's offical documentation.

index.js:

var nfc = require("ti.nfc");
var nfcAdapter;

// This creates an NFC adapter associated with the current activity. 
// Each activity should have only ONE NFC adapter.
nfcAdapter = nfc.createNfcAdapter({
     onNdefDiscovered: handleNDEFDiscovery,
     onTagDiscovered: handleTagDiscovery,
     onTechDiscovered: handleTechDiscovery
});


function handleNDEFDiscovery(data){
    // alert('NDEF DISCOVERED:: ' + data.messages[0].records[0].getPayload());
    // Our required payload fits in the first record of the first Ndef msg
    var payload = data.messages[0].records[0].getPayload();
    // First byte of payload is control byte
    // Bits 5 to 0 of the control bytes contain 
    // length of language code succeeding the control byte
    var langCodeLength = payload[0] & 0077;
    // Received NFC text data starts after textOffset bytes in payload
    var textOffset = langCodeLength + 1;
    // Payload contains byte array which needs to be converted to a string
    // nfc_text contains the exact text string sent by the sending NFC device
    var nfc_text = payload.toString().substring(textOffset);
    /* process nfc_text as required by application logic here */
}

function handleTagDiscovery(data){
    alert('TAG DISCOVERED:: ' + data.messages[0].records[0].getPayload());
}

function handleTechDiscovery(data){
    alert('TECH DISCOVERED:: ' + data.messages[0].records[0].getPayload());
}

    // Check for NFC support on device
    if (!nfcAdapter.isEnabled()) {
        alert('NFC is not enabled on this device!');
    }else{        
        // Tag scans are received by the current activity as new intents. We
        // need to pass scan intents to the nfc adapter for processing.
        var act = Ti.Android.currentActivity;
        act.addEventListener('newintent', function(e) {
            nfcAdapter.onNewIntent(e.intent);
        });

        // Since we want the app to only use NFC while active in the foreground
        // We disable and enable foreground dispatch on app pause and resume respectively
        act.addEventListener('resume', function(e) {
            nfcAdapter.enableForegroundDispatch(dispatchFilter);
        });
        act.addEventListener('pause', function(e) {
            nfcAdapter.disableForegroundDispatch();
        });

        // The foreground dispatch filter specifies the types of NFC 
        // messages the app wants to receive and handle. The only entry in our case
        // specifies type ‘text/plain’ since we want to send and receive plain text
        dispatchFilter = nfc.createNfcForegroundDispatchFilter({
            intentFilters: [
                { action: nfc.ACTION_NDEF_DISCOVERED, mimeType: 'text/plain' },
            ],
            techLists: [
                [ "android.nfc.tech.NfcF" ],
                [ "android.nfc.tech.Ndef" ],
                [ "android.nfc.tech.MifareClassic" ],
                [ "android.nfc.tech.NfcA" ]
            ]
        });
        // This enables foreground dispatch for the first time
        nfcAdapter.enableForegroundDispatch(dispatchFilter);
    }

Android tiapp.xml:

<android xmlns:android="http://schemas.android.com/apk/res/android">
    <manifest>
<uses-permission android:name="android.permission.NFC"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-sdk android:minSdkVersion="14" />
        <application>
            <activity
             android:label="TagViewer" android:theme="@style/Theme.Titanium"
             android:configChanges="keyboardHidden|orientation"
             android:launchMode="singleTask">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <data android:mimeType="text/plain"/>
                </intent-filter>
                <intent-filter>
                    <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                    <data android:scheme="http"/>
                </intent-filter>
            </activity>
        </application>
    </manifest>
</android>

When I start the app, the nfcAdapter is enabled and I see these 4 alert:

[INFO] :   ALERT: (KrollRuntimeThread) [322,322] org.appcelerator.titanium.proxy.ActivityProxy@1a252dad
[INFO] :   ALERT: (KrollRuntimeThread) [0,322] org.appcelerator.kroll.runtime.v8.V8Function@368d58e2
[INFO] :   ALERT: (KrollRuntimeThread) [0,322] org.appcelerator.kroll.runtime.v8.V8Function@2c0fe473
[INFO] :   ALERT: (KrollRuntimeThread) [1,323] org.appcelerator.kroll.runtime.v8.V8Function@3e517f30

And everytime that I hold a tag(card against the back of the phone I see in the console of appcelerator something like this:

[INFO] :   Timeline: Timeline: Activity_idle id: android.os.BinderProxy@30a1f176 time:23801469
[INFO] :   Timeline: Timeline: Activity_idle id: android.os.BinderProxy@30a1f176 time:23802093
etc...

I used another App that I found on the Play store (NFC Reader). The data that my Tag contain are: Tag ID (hex), Tag ID (dec), ID (reversed). The tag that I use has these characteristics:

  • Type of tag: ISO 15693

  • Tecnology: NfcV, NdefFormatable

  • Serial number: a2:3b:6b:1e:50:01:04:e0

Output from NFC Reader What is the problem? Why it doesn't interpret good the tag/card? I hope you can help me.

Thanks a lot ;)


Solution

  • Given the output from NFC Reader, your tag does not contain an NDEF message (since it displays "NdefFormatable" but not "Ndef" tag technology). However, you registered the NFC intent filter (both, in the manifest and for the foreground dispatch) to only be sensitive for NFC tags that contain either a Text record (or MIME type "text/plain") or a "http://" URL (registered in manifest only):

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="http"/>
    </intent-filter>
    
    nfc.createNfcForegroundDispatchFilter({
            intentFilters: [
                { action: nfc.ACTION_NDEF_DISCOVERED, mimeType: 'text/plain' },
            ],
            ...
    

    Consequently, your app won't pick up the tag.

    What can you do about this?

    Either you program the tag with the data types expected by your app (thus, you write a Text record onto the tag), or you change the intent filters of your app to properly discover that tag type.

    For the foreground dispatch, you can achieve this with this code:

    dispatchFilter = nfc.createNfcForegroundDispatchFilter({
        intentFilters: [
            { action: nfc.ACTION_TECH_DISCOVERED },
        ],
        techLists: [
            [ "android.nfc.tech.NfcV" ]
        ]
    }); 
    

    You will then receive the tag discovery event in your onTechDiscovered callback:

    function handleTechDiscovery(data) {
        // do something
    }
    

    You can then process the tag based on your needs. E.g. if you want to read the tag UID/anti-collision identifier, you could obtain the NfcTag object from the data parameter:

        var tag = data.tag;
    

    And get the UID by querying the getId() method:

        alert('Tag ID: ' + tag.getId());