Java Question Adding new functionalities and tags decoding to NFC library

Sherlock

Member
Licensed User
Longtime User
Hello Erel,

Could you post the NFC library source code so we could add methods and support for TECH and TAG tags as per the standard?

Thanks

Pierre
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Here:
B4X:
package anywheresoftware.b4a.objects;

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.util.Arrays;

import android.content.Intent;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Parcelable;
import anywheresoftware.b4a.AbsObjectWrapper;
import anywheresoftware.b4a.BA.Permissions;
import anywheresoftware.b4a.BA.ShortName;
import anywheresoftware.b4a.BA.Version;
import anywheresoftware.b4a.objects.collections.List;

/**
 * Supports reading NDEF (NFC Data Exchange Format) tags.
 *See this <link>tutorial|http://www.b4x.com/forum/basic4android-getting-started-tutorials/14931-reading-ndef-data-nfc-tags.html</link> for more information.
 */
@Version(1.0f)
@ShortName("NFC")
@Permissions(values={"android.permission.NFC"})
public class NFC {
   /**
    * Tests whether the Intent contains data read from an NDef tag.
    */
   public boolean IsNdefIntent(Intent Intent) {
      if (Intent == null)
         return false;
      return Intent.hasExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
   }
   /**
    * Retrieves the NdefRecords stored in the Intent object.
    */
   public List GetNdefRecords(Intent Intent) {
      List l = new List();
      l.Initialize();
      Parcelable[] rawMsgs = Intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
      if (rawMsgs != null) {
         for (int i = 0; i < rawMsgs.length; i++) {
            NdefMessage nm = (NdefMessage) rawMsgs[i];
            for (NdefRecord r : nm.getRecords()) {
               l.Add( r);
            }
         }
      }
      return l;
   }
   @ShortName("NdefRecord")
   public static class NdefRecordWrapper extends AbsObjectWrapper<NdefRecord> {
      /**
       * Returns the whole payload.
       */
      public byte[] GetPayload() {
         return getObject().getPayload();
      }
      
      private static java.util.List<String> UriTypes = Arrays.asList(
            ""
            , "http://www."
            , "https://www."
            , "http://"
            , "https://"
            , "tel:"
            , "mailto:"
            , "ftp://anonymous:anonymous@"
            , "ftp://ftp."
            , "ftps://"
            , "sftp://"
            , "smb://"
            , "nfs://"
            , "ftp://"
            , "dav://"
            , "news:"
            , "telnet://"
            , "imap:"
            , "rtsp://"
            , "urn:"
            , "pop:"
            , "sip:"
            , "sips:"
            , "tftp:"
            , "btspp://"
            , "btl2cap://"
            , "btgoep://"
            , "tcpobex://"
            , "irdaobex://"
            , "file://"
            , "urn:epc:id:"
            , "urn:epc:tag:"
            , "urn:epc:pat:"
            , "urn:epc:raw:"
            , "urn:epc:"
            , "urn:nfc:");

      /**
       * Reads the payload and returns the stored text.
       */
      public String GetAsTextType() throws UnsupportedEncodingException {

         byte[] payload = getObject().getPayload();
         String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
         int languageCodeLength = payload[0] & 0077;
         @SuppressWarnings("unused")
         String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
         String text =
            new String(payload, languageCodeLength + 1,
                  payload.length - languageCodeLength - 1, textEncoding);
         return text;
      }
      /**
       * Reads the payload and returns the stored Uri.
       */
      public String GetAsUriType() {
         byte[] payload = getObject().getPayload();
         String prefix = UriTypes.get(payload[0]);
         byte[] prefixBytes = prefix.getBytes(Charset.forName("UTF-8"));
         
         byte[] fullUri = new byte[prefixBytes.length + payload.length - 1];
         System.arraycopy(prefixBytes, 0, fullUri, 0, prefixBytes.length);
         System.arraycopy(payload, 1, fullUri, prefixBytes.length, payload.length - 1);
         Uri uri = Uri.parse(new String(fullUri, Charset.forName("UTF-8")));
         return uri.toString();
      }
   }
}
 

Sherlock

Member
Licensed User
Longtime User
Thanks Erel,

I would like to get access to NFC raw data. The whole payload, not just NDEF.

I think it would be coming from AbsObjectWrapper package but I don't have any knowledge of its content.

Could you point me to a method that I could call?

Thanks
 

Sherlock

Member
Licensed User
Longtime User
Here's what I've added so far :

In class NFC:
/*
* Return Extra Tag
*/
public String GetExtraTag (Intent Intent) {
return NfcAdapter.EXTRA_TAG;
}

/*
* Return NFC tag UID
*/
public byte [] GeTagtUID (Intent Intent) {
return Intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
}

in class NdefRecordWrapper:

/*
* Returns the variable length Type field
*/
public byte [] GetType() {
byte [] Type = getObject().getType();
return Type;
}

/*
* Return the 3 bits Tnf
*/
public short GetTnf () {
return getObject().getTnf();
}

/*
* Return the entire NDEF Record as a byte array
*/
public byte [] GetNdefByteArray () {
return getObject().toByteArray();
}

/*
* Return True if Tag is of Uri type
*/
public boolean IsUriType () {
byte [] Type = getObject().getType();
if (Type [0] == 0x55)
return true;
return false;
}

/*
* Return True if Tag is of Text type
*/
public boolean IsTextType () {
byte [] Type = getObject().getType();
if (Type [0] == 0x54)
return true;
return false;
}
 

Sherlock

Member
Licensed User
Longtime User
NFC library V1.1 - Give it a try and let me know

Here's the file:
 

Attachments

  • NFC Version 1.1.zip
    6 KB · Views: 687

Christian321

Member
Licensed User
Longtime User
NFC peer-2-peer mode

(1) I am an absolute newbee to android programming.
(2) By any chance, is there someone around trying to make the NFC peer-2-peer mode work with B4A? My intention is to set up a demo application that can communicate between two devices.
(3) Did I get this right, if I need a function that is provided by the android OS but not available in B4A I will have to write a java module, compile this with the help of the Java SDK and put it into the B4A folder? Or to put it this way: I will still need Java to have full access to the OS functions?

Many thanks in advance,
Christian

:sign0085:
 

salmander

Active Member
Licensed User
Longtime User
(1) I am an absolute newbee to android programming.
(2) By any chance, is there someone around trying to make the NFC peer-2-peer mode work with B4A? My intention is to set up a demo application that can communicate between two devices.
(3) Did I get this right, if I need a function that is provided by the android OS but not available in B4A I will have to write a java module, compile this with the help of the Java SDK and put it into the B4A folder? Or to put it this way: I will still need Java to have full access to the OS functions?

Many thanks in advance,
Christian

:sign0085:
What you are looking for is NFC Beaming. This feature is not yet implemented in B4A.
 

jjcc

Member
Licensed User
Longtime User
Here's the file:

Thank you Sherlock

I've tested version 1.1. The text record tag works fine.

But when I use URI tag, the application can not pick up any event.

Did I miss anything?

B4X:
Sub Activity_Resume
   Dim rString As String
   Dim bType() As Byte
   Dim bPayload As Byte
   Dim rPack As PackStruct
   rPack.Valid = False
   Log("Main Resume called..")
    If NFC.IsNdefIntent(Activity.GetStartingIntent) Then
      Log("NFC tag found.....")
        Dim records As List
        records = NFC.GetNdefRecords(Activity.GetStartingIntent)
      rPack = DataProcessing.ParsingNDEF(records)
      ToastMessageShow("Reading tag....",True)

    End If
End Sub
 
Last edited:

Rafal Galewski

Member
Licensed User
Longtime User
UID read Mifare 1 tag

How can I use Your library NFC 1.1 to read correctly UID from Mifare 1?
How create correctly manifest .

Please help.
 

raphael75

Active Member
Licensed User
Longtime User
Here's what I've added so far :

B4X:
In class NFC:

    /*
     * Return NFC tag UID
     */
    public byte [] GeTagtUID (Intent Intent) {
       return Intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
    }

I suggest that you rename GeTagtUID to GetTagUID (move the "t") :)
 

Rafal Galewski

Member
Licensed User
Longtime User
Android 4.1.1 UID can't be read

Hi,

Thank You for answer.
This option work in android 4.0.4
But in 4.1.1 this option no work.

Becouse is null exception whn we try read no formatted tag.

I have to return to 4.0.4 android.

Maybe can look this problem.
 

Rafal Galewski

Member
Licensed User
Longtime User
How read secure TAG and sectores on it.

When I would like change key A and key B to security sector on TAG.

How can I set this security key A and B for sectores using Your library ?

Antoher topic is how can I read specyfic sectores from TAG or Mifare 1 K .

I choose sector 16 and I read selected data from it.

Could You add this functionality ?
 

jjcc

Member
Licensed User
Longtime User
as Erel said:
If you need a feature that is not covered by any library then you will need to implement a Java library.

There are several tutorial available for creation of new libraries.

The functions you need is not covered by this library. I might write one to deal with Mifare classic card. But for now I'm busy. Should be finished within a few weeks.
 

Rafal Galewski

Member
Licensed User
Longtime User
New function in library key A , key B read data from sector

Please add this functionality to next version library.

Sorry I can't write library,
Idon't use Java now.
:sign0013:
Thank You very much
 

jjcc

Member
Licensed User
Longtime User
The attachments are the new version of NFC library and a sample application. I added some extra java code to the library by Sherlok and Erel.

Here's how to use the lib:
1.initialize a "MiFare" object.
2.Call ReadSector() method with 3 parameters: SectorIndex, KeyData, KeyType
3.If succeeded, retrieve the data block by block with method GetBlockData()

B4X:
Sub Read()
   Dim key(16) As Byte 
   Dim block_data() As Byte
   Dim bc As ByteConverter
   
   Dim i As Int
   For i  = 0 To 15
      key(i) = 255 '0xff
   Next

   'btnClear
   Dim mfc As MiFare
   Log("Check if the card is MF classic")
    If NFC.IsMifareClassic(Activity.GetStartingIntent) Then
      Dim StrData As String
      mfc.Initialize(Activity.GetStartingIntent)
      If True = mfc.ReadSector(0,key,0) Then' Read sector 0, key, type 0(as keyA)      
         Log("Read sector done")
         For i = 0 To 2 'read 3 blocks of data
            block_data = mfc.GetBlockData(i)
            StrData =  bc.HexFromBytes(block_data)
            Log( "data:" & StrData)
            If i = 0 Then
               Label1.Text = StrData
            Else If i = 1 Then
               Label2.Text = StrData
            Else If i = 2 Then
               Label3.Text = StrData
            End If
         Next
      End If
   End If
End Sub

In manifest editor, I added "android.nfc.action.NDEF_DISCOVERED" and "android.nfc.action.TAG_DISCOVERED" intent filter. Actually "android.nfc.action.TECH_DISCOVERED" is better than NDEF_DISCOVERED but it needs some code like:
B4X:
  <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
          android:resource="@xml/filter_nfc" />

I don't know how to put the "filter_nfc.xml" under "res/xml" directory. Maybe Erel knows.

Good luck
 

Attachments

  • NFC_1_21.zip
    8.6 KB · Views: 626
  • MifarelReader.zip
    97.1 KB · Views: 706
Top