jeudi 4 octobre 2012

Read/Write NFC tag in Android

Android & NFC howto

When a NFC tag is discovered, the system checks if the foreground application has registered to such an Intent with a call to enableForegroundDispatch(). If not, it checks whether any other applications has registered for the NDEF_DISCOVERED intent. If an application want to register for events related to a tag of a specific technology (e.g. classic MiFare tags) then it should register to the TECH_DISCOVERED intent.
The following diagram explains in details how an event related to reading a tag is handled:

Read NFC tag in Android

Declare in the manifest a specific Activity to receive an Intent when a NFC tag is read by the device:
<activity android:name=".NfcReaderActivity" android:label="NfcReaderActivity">
    <intent-filter>
     <action android:name="android.nfc.action.TAG_DISCOVERED"/>
     <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</activity>

To register the foreground Activity for tag reading events, add following lines to onResume() method:
@Override
protected onResume() {
  PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
  mAdapter.enableForegroundDispatch(this, pendingIntent, null, null);
} 

In the onNewIntent(Intent)to read the tag after receiving a ACTION_TAG_DISCOVERED intent :
protected void onNewIntent(Intent intent) {
  String action = intent.getAction();
  if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)){
    Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    NdefMessage[] messages;
    if (rawMsgs != null) {
      messages = new NdefMessage[rawMsgs.length];
      for (int i = 0; i < rawMsgs.length; i++) {
        messages[i] = (NdefMessage) rawMsgs[i];     
        // To get a NdefRecord and its different properties from a NdefMesssage   
     NdefRecord record = messages[i].getRecords()[i];
     byte[] id = record.getId();
     short tnf = record.getTnf();
     byte[] type = record.getType();
     String message = getTextData(record.getPayload());
      }
    }
  }
}

This is how to read the content tag of type text RTD_TEXT
// Decoding a payload containing text
private String getTextData(byte[] payload) {
  if(payload == null) 
    return null;
  try {
    String encoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
    int langageCodeLength = payload[0] & 0077;
    return new String(payload, langageCodeLength + 1, payload.length - langageCodeLength - 1, encoding);     
  } catch(Exception e) {
    e.printStackTrace();
  }
  return null;
}

Write NFC tag in Android

In the manifest:
<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
in onCreate() get the NfcAdapter:
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
The data part of a TAG is a NdefMessage of multiple NdefRecord this is how to encode a text into a payload:
// Encoding a text into a tag payload 
NdefRecord createRecord(String message) {
  byte[] langBytes = Locale.ENGLISH.getLanguage().getBytes(Charset.forName("US-ASCII"));
  byte[] textBytes = message.getBytes(Charset.forName("UTF-8"));
  char status = (char) (langBytes.length);
  byte[] data = new byte[1 + langBytes.length + textBytes.length];
  data[0] = (byte) status;
  System.arraycopy(langBytes, 0, data, 1, langBytes.length);
  System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
  return new NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, new byte[0], data);
}
// Create a message from a record
NdefMessage createMessage(NdefRecord record) {
  NdefRecord[] records = new NdefRecord[1];
  records[0] = record;
  NdefMessage message = new NdefMessage(records);
  return message;
}
To enable/disable sending a message each time a tag is read
@Override
protected void onPause() {
  super.onPause();
  nfcAdapter.disableForegroundNdefPush(this);
}
@Override
protected void onResume() {
  super.onResume();
  NdefRecord record = creerRecord("this is a message");
  NdefMessage message = creerMessage(record);
  nfcAdapter.enableForegroundNdefPush(this, message);
}
If there is no tag, we can simulate a tag been read by sending the corresponding intent, this is how:
final Intent intent = new Intent(NfcAdapter.ACTION_TAG_DISCOVERED);
NdefMessage[] messages = new NdefMessage[1];
messages[0] = createMessage(createRecord("Simulating a tag reception"));
intent.putExtra(NfcAdapter.EXTRA_NDEF_MESSAGES, messages);
startActivity(intent); 

Reference Programming Android, chapter 16: Sensors, NFC, Speech, Gestures, and Accessibility.

1 commentaire:

  1. I actually enjoyed reading through this posting.Many thanks.Good article! We are linking to this great article on our site. Keep up the good writing.
    website design

    RépondreSupprimer