package com.oreilly.demo.android.pa.sensordemo;
import java.nio.charset.Charset;
import java.util.Arrays;
import android.app.Activity;
import android.content.Intent;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.TextView;
public class NFC extends Activity {
private static enum NFCType {
UNKNOWN, TEXT, URI, SMART_POSTER, ABSOLUTE_URI
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.nfc);
setTitle("Near Field Communication");
findViewById(R.id.close).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
analyzeIntent(getIntent());
}
private void analyzeIntent(final Intent intent) {
if(intent == null) return;
String id = getTagId(intent);
NFCType type = NFCType.UNKNOWN;
String datastr = null;
byte[] data = null;
NdefMessage tag = getTagData(intent);
if(tag != null) {
type = getTagType(tag);
if(type != NFCType.UNKNOWN) {
datastr = getTagData(tag);
} else data = getTagRawData(tag);
}
if(datastr != null) updateViewInfo(id, type, datastr);
else updateViewInfo(id, type, data);
}
private void updateViewInfo(String id, NFCType type, byte[] data) {
updateViewInfo(id, type, data != null ? getHexString(data) : null);
}
private void updateViewInfo(String id, NFCType type, String data) {
if(id != null) {
((TextView) findViewById(R.id.tagid)).setText("TagID: "+id);
}
if(type != NFCType.UNKNOWN) {
String typestr = "";
switch(type) {
case TEXT: typestr = "Text"; break;
case SMART_POSTER: typestr = "Smart Poster"; break;
case URI: typestr = "URI"; break;
case ABSOLUTE_URI: typestr = "URI (Abs)"; break;
default: typestr = "UNKNOWN";
break;
}
((TextView) findViewById(R.id.tagtype)).setText("TagType: "+typestr);
}
if(data != null) {
((TextView) findViewById(R.id.tagdata)).setText("TagData:\n"+data);
}
}
private String getTagId(final Intent intent) {
if(intent == null) return null;
byte[] byte_id = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
if(byte_id == null) return null;
return getHexString(byte_id);
}
private NdefMessage getTagData(final Intent intent) {
if(intent == null || !intent.hasExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)) return null;
Parcelable[] msgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if(msgs == null || msgs.length < 1) {
return null;
}
NdefMessage[] nmsgs = new NdefMessage[msgs.length];
for(int i=0;i<msgs.length;i++) {
nmsgs[i] = (NdefMessage) msgs[i];
}
// we will only grab the first msg as we are handling only 1 tag at the moment
return nmsgs[0];
}
private NFCType getTagType(final NdefMessage msg) {
if(msg == null) return null;
// we are only grabbing the first recognizable item
for (NdefRecord record : msg.getRecords()) {
if(record.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
if(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
return NFCType.TEXT;
}
if(Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
return NFCType.URI;
}
if(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
return NFCType.SMART_POSTER;
}
} else if(record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) {
return NFCType.ABSOLUTE_URI;
}
}
return null;
}
private String getTagData(final NdefMessage msg) {
if(msg == null) return null;
// we are only grabbing the first recognizable item
for(NdefRecord record : msg.getRecords()) {
if(record.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
if(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
return getText(record.getPayload());
}
if(Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
return getURI(record.getPayload());
}
if(Arrays.equals(record.getType(), NdefRecord.RTD_SMART_POSTER)) {
if(record.getPayload() == null || record.getPayload().length < 1) return null;
try {
NdefMessage subrecords = new NdefMessage(record.getPayload());
return getSubRecordData(subrecords.getRecords());
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
} else if(record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) {
return getAbsoluteURI(record.getPayload());
}
}
return null;
}
private String getSubRecordData(final NdefRecord[] records) {
if(records == null || records.length < 1) return null;
String data = "";
for(NdefRecord record : records) {
if(record.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
if(Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
data += getText(record.getPayload()) + "\n";
}
if(Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
data += getURI(record.getPayload()) + "\n";
} else {
data += "OTHER KNOWN DATA\n";
}
} else if(record.getTnf() == NdefRecord.TNF_ABSOLUTE_URI) {
data += getAbsoluteURI(record.getPayload()) + "\n";
} else data += "OTHER UNKNOW DATA\n";
}
return data;
}
private byte[] getTagRawData(final NdefMessage msg) {
if(msg == null || msg.getRecords().length < 1) return null;
// we are only grabbing the first item
return msg.getRecords()[0].getPayload();
}
/*
* the First Byte of the payload contains the "Status Byte Encodings" field, per the NFC Forum "Text Record Type Definition" section 3.2.1.
*
* Bit_7 is the Text Encoding Field.
* * if Bit_7 == 0 the the text is encoded in UTF-8 else if Bit_7 == 1 then the text is encoded in UTF16
* Bit_6 is currently always 0 (reserved for future use)
* Bits 5 to 0 are the length of the IANA language code.
*/
private String getText(final byte[] payload) {
if(payload == null) return null;
try {
String textEncoding = ((payload[0] & 0200) == 0) ? "UTF-8" : "UTF-16";
int languageCodeLength = payload[0] & 0077;
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private String getAbsoluteURI(final byte[] payload) {
if(payload == null) return null;
return new String(payload, Charset.forName("UTF-8"));
}
/**
* the First Byte of the payload contains the prefix byte
*/
private String getURI(final byte[] payload) {
if(payload == null || payload.length < 1) return null;
String prefix = convertUriPrefix(payload[0]);
return (prefix != null ? prefix : "" ) + new String(Arrays.copyOfRange(payload, 1,payload.length));
}
/**
* NFC Forum "URI Record Type Definition"
*
* Conversion of prefix based on section 3.2.2 of the NFC Forum URI Record Type Definition document.
*/
private String convertUriPrefix(final byte prefix) {
if(prefix == (byte) 0x00) return "";
else if(prefix == (byte) 0x01) return "http://www.";
else if(prefix == (byte) 0x02) return "https://www.";
else if(prefix == (byte) 0x03) return "http://";
else if(prefix == (byte) 0x04) return "https://";
else if(prefix == (byte) 0x05) return "tel:";
else if(prefix == (byte) 0x06) return "mailto:";
else if(prefix == (byte) 0x07) return "ftp://anonymous:anonymous@";
else if(prefix == (byte) 0x08) return "ftp://ftp.";
else if(prefix == (byte) 0x09) return "ftps://";
else if(prefix == (byte) 0x0A) return "sftp://";
else if(prefix == (byte) 0x0B) return "smb://";
else if(prefix == (byte) 0x0C) return "nfs://";
else if(prefix == (byte) 0x0D) return "ftp://";
else if(prefix == (byte) 0x0E) return "dav://";
else if(prefix == (byte) 0x0F) return "news:";
else if(prefix == (byte) 0x10) return "telnet://";
else if(prefix == (byte) 0x11) return "imap:";
else if(prefix == (byte) 0x12) return "rtsp://";
else if(prefix == (byte) 0x13) return "urn:";
else if(prefix == (byte) 0x14) return "pop:";
else if(prefix == (byte) 0x15) return "sip:";
else if(prefix == (byte) 0x16) return "sips:";
else if(prefix == (byte) 0x17) return "tftp:";
else if(prefix == (byte) 0x18) return "btspp://";
else if(prefix == (byte) 0x19) return "btl2cap://";
else if(prefix == (byte) 0x1A) return "btgoep://";
else if(prefix == (byte) 0x1B) return "tcpobex://";
else if(prefix == (byte) 0x1C) return "irdaobex://";
else if(prefix == (byte) 0x1D) return "file://";
else if(prefix == (byte) 0x1E) return "urn:epc:id:";
else if(prefix == (byte) 0x1F) return "urn:epc:tag:";
else if(prefix == (byte) 0x20) return "urn:epc:pat:";
else if(prefix == (byte) 0x21) return "urn:epc:raw:";
else if(prefix == (byte) 0x22) return "urn:epc:";
else if(prefix == (byte) 0x23) return "urn:nfc:";
return null;
}
private final static char[] HEX = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
// convert bytes to a hex string
private static String getHexString(final byte[] bytes) {
StringBuffer hex = new StringBuffer(bytes.length * 2);
for (int i = 0; i < bytes.length; i++) {
for (int j = 1; j >= 0; j--) {
hex.append(HEX[(bytes[i] >> (j * 4)) & 0xF]);
}
}
return hex.toString();
}
}