package com.integreight.onesheeld.shields.controller;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.nfc.tech.NdefFormatable;
import android.os.Build;
import android.provider.Settings;
import com.integreight.onesheeld.sdk.ShieldFrame;
import com.integreight.onesheeld.R;
import com.integreight.onesheeld.enums.UIShield;
import com.integreight.onesheeld.shields.ControllerParent;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Created by Mouso on 3/11/2015.
*/
public class NfcShield extends ControllerParent<NfcShield> {
private static final byte SHIELD_ID = UIShield.NFC_SHIELD.getId();
private static final byte RECORD_QUERY_DATA = 0x01;
private static final byte RECORD_QUERY_PARSED_DATA = 0x03;
private static final byte RECORD_QUERY_TYPE = 0x02;
private static final byte NEW_TAG_FRAME = 0x01;
private static final byte TAG_ERROR_FRAME = 0x02;
private static final byte RECORD_QUERY_TYPE_FRAME = 0x03;
private static final byte RECORD_QUERY_PARSED_DATA_FRAME = 0x04;
private static final byte RECORD_QUERY_DATA_FRAME = 0x05;
private static final int UNKNOWN_TYPE = 0;
private static final int EMPTY_TYPE = 1;
private static final int EXTERNAL_TYPE = 2;
private static final int MIME_MEDIA_TYPE = 3;
private static final int UNCHANGED_TYPE = 4;
private static final int ABSOLUTE_URI_TYPE = 5;
private static final int TEXT_TYPE = 6;
private static final int URI_TYPE = 7;
private static final int UNSUPPORTED_TYPE = 8;
private static final byte TAG_NOT_SUPPORTED = 0x02;
private static final byte RECORD_CAN_NOT_BE_PARSED = 0x01;
private static final byte INDEX_OUT_OF_BOUNDS = 0x00;
private static final byte NO_ENOUGH_BYTES = 0x03;
private static final byte TAG_READING_ERROR = 0x04;
private static final byte RECORD_NOT_FOUND = 0x05;
private NFCEventHandler eventHandler;
private Tag currentTag;
private boolean isNdef_Flag = false;
private boolean isTagSupported = false;
private boolean isForeground = false;
private static final String[] UriTypes = {"", "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:"};
public NfcShield() {
}
public NfcShield(Activity activity, String tag) {
super(activity, tag);
}
public void setCurrentTag(Tag currentTag) {
this.currentTag = currentTag;
}
@Override
public ControllerParent<NfcShield> invalidate(
com.integreight.onesheeld.shields.ControllerParent.SelectionAction selectionAction,
boolean isToastable) {
this.selectionAction = selectionAction;
registerNFCListener(isToastable);
return super.invalidate(selectionAction, isToastable);
}
public void registerNFCListener(boolean isToastable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null && nfcAdapter.isEnabled()) {
setupForegroundDispatch();
selectionAction.onSuccess();
} else {
if(nfcAdapter == null){
if (isToastable) {
activity.showToast(R.string.nfc_device_doesnt_support_nfc);
}
}
else {
showSettingsDialogIfNfcIsNotEnabled();
}
selectionAction.onFailure();
}
} else {
if (isToastable)
activity.showToast(R.string.nfc_device_doesnt_support_nfc);
selectionAction.onFailure();
}
}
public void showSettingsDialogIfNfcIsNotEnabled(){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null && !nfcAdapter.isEnabled()) {
AlertDialog.Builder alertbox = new AlertDialog.Builder(getActivity());
alertbox.setMessage(R.string.nfc_we_need_you_to_enable_nfc_for_this_shield_to_work);
alertbox.setPositiveButton(R.string.nfc_validation_dialog_ok_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
Intent intent = new Intent(Settings.ACTION_NFC_SETTINGS);
getActivity().startActivity(intent);
} else {
Intent intent = new Intent(Settings.ACTION_WIRELESS_SETTINGS);
getActivity().startActivity(intent);
}
}
});
alertbox.setNegativeButton(R.string.nfc_validation_dialog_later_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
activity.showToast(activity.getString(R.string.nfc_please_enable_nfc_to_be_able_to_use_this_shield));
}
});
alertbox.show();
}
}
}
@Override
public void onNewShieldFrameReceived(ShieldFrame frame) {
if (frame.getShieldId() == SHIELD_ID) {
if (frame.getArguments() != null && frame.getArguments().size() > 0) {
int record, start, size;
DataReply data;
ShieldFrame sf;
if (isNdef_Flag) {
switch (frame.getFunctionId()) {
case RECORD_QUERY_DATA:
record = frame.getArgumentAsInteger(0);
start = frame.getArgumentAsInteger(1);
size = frame.getArgumentAsInteger(2);
data = readNdefRecordData(record, start, size,255);
if (!data.hasError() || data.getError() == NO_ENOUGH_BYTES) {
sf = new ShieldFrame(SHIELD_ID, RECORD_QUERY_DATA_FRAME);
sf.addArgument(1, record);
sf.addArgument(data.getBytesData());
sendShieldFrame(sf, true);
}
if (data.hasError()){
sendError(data.getError(),data.getError() == NO_ENOUGH_BYTES);
}
break;
case RECORD_QUERY_PARSED_DATA:
record = frame.getArgumentAsInteger(0);
data = readNdefRecordParsedData(record, 0, 255,255);
if (!data.hasError() || data.getError() == NO_ENOUGH_BYTES) {
sf = new ShieldFrame(SHIELD_ID, RECORD_QUERY_PARSED_DATA_FRAME);
sf.addArgument(1, record);
sf.addArgument(data.getBytesData());
sendShieldFrame(sf, true);
}
if (data.hasError() && data.getError() != NO_ENOUGH_BYTES){
sendError(data.getError());
}
break;
case RECORD_QUERY_TYPE:
record = frame.getArgumentAsInteger(0);
start = frame.getArgumentAsInteger(1);
size = frame.getArgumentAsInteger(2);
data = readNdefRecordType(record, start, size,255);
if (!data.hasError() || data.getError() == NO_ENOUGH_BYTES) {
sf = new ShieldFrame(SHIELD_ID, RECORD_QUERY_TYPE_FRAME);
sf.addArgument(1, record);
sf.addArgument(data.getBytesData());
sendShieldFrame(sf, true);
}
if (data.hasError()){
sendError(data.getError(),data.getError() == NO_ENOUGH_BYTES);
}
break;
default:
break;
}
} else {
sendError(TAG_READING_ERROR);
}
}
}
}
@Override
public void reset() {
if (isForeground)
stopForegroundDispatch();
else {
PackageManager packageManager = activity.getApplicationContext().getPackageManager();
packageManager.setComponentEnabledSetting(new ComponentName("com.integreight.onesheeld", "com.integreight.onesheeld.NFCUtils-alias"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
}
}
public NFCEventHandler getEventHandler() {
return eventHandler;
}
public void setEventHandler(NFCEventHandler eventHandler) {
this.eventHandler = eventHandler;
}
public static interface NFCEventHandler {
void ReadNdef(String id, int maxSize, int usedSize, ArrayList<ArrayList<String>> data);
}
private void resetTechnologyFlags() {
isTagSupported = false;
isNdef_Flag = false;
}
public void setupForegroundDispatch() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);
String[][] techList = new String[3][1];
techList[0][0] = "android.nfc.tech.NdefFormatable";
techList[1][0] = "android.nfc.tech.NfcA";
techList[2][0] = "android.nfc.tech.Ndef";
IntentFilter[] filters = new IntentFilter[2];
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_TECH_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
filters[1] = new IntentFilter();
filters[1].addAction(NfcAdapter.ACTION_TAG_DISCOVERED);
filters[1].addCategory(Intent.CATEGORY_DEFAULT);
if (nfcAdapter != null) {
if (!isForeground) {
nfcAdapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
isForeground = true;
}
}
}
}
public void stopForegroundDispatch() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(activity);
if (nfcAdapter != null) {
if (isForeground) {
nfcAdapter.disableForegroundDispatch(activity);
isForeground = false;
}
}
}
}
public void handleIntent(Intent intent) {
String action = intent.getAction();
if (action == null) return;
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
if (tag == null) {
sendError(TAG_READING_ERROR);
}else {
resetTechnologyFlags();
switch (action) {
case NfcAdapter.ACTION_NDEF_DISCOVERED:
setCurrentTag(tag);
if (!getNdefMaxSize().hasError() && !getTagId().hasError()) {
isTagSupported = true;
isNdef_Flag = true;
displayData();
sendNewTagFrame();
} else {
sendError(TAG_READING_ERROR);
}
break;
case NfcAdapter.ACTION_TECH_DISCOVERED:
setCurrentTag(tag);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
String[] techList = tag.getTechList();
for (String tech : techList) {
if (!isTagSupported) {
if (Ndef.class.getName().equals(tech)) {
DataReply maxSize = getNdefMaxSize();
if (!maxSize.hasError() && !getTagId().hasError()) {
isNdef_Flag = false;
isTagSupported = true;
DataReply recordCount = getNdefRecordCount();
if (recordCount.getIntegerData() > 0 && recordCount.getIntegerData() < 256) {
isNdef_Flag = true;
displayData();
sendNewTagFrame();
}else if (recordCount.getIntegerData() == 0){
sendNewEmptyTagFrame();
}else if(recordCount.getError() != 0){
sendError(recordCount.getError());
}
} else if (maxSize.hasError()){
sendError(maxSize.getError());
}
} else if (NdefFormatable.class.getName().equals(tech)) {
/*isNdef_Flag = false;
isTagSupported = true;
if (!getNdefMaxSize().hasError()&& !getTagId().hasError()) {
sendNewEmptyTagFrame();
displayData();
} else {
sendError(TAG_READING_ERROR);
}*/
}
} else
break;
}
if (!isTagSupported)
sendError(TAG_NOT_SUPPORTED);
}
break;
case NfcAdapter.ACTION_TAG_DISCOVERED:
setCurrentTag(tag);
break;
}
}
}
public void displayData() {
if (eventHandler != null)
if (isNdef_Flag && currentTag != null) {
DataReply tagId = getTagId();
DataReply ndefMaxSize = getNdefMaxSize();
DataReply ndefUsedSize = getNdefUsedSize();
ArrayList<ArrayList<String>> arrayListForDisplay = generateArrayListForDisplay();
if (!tagId.hasError() && !ndefMaxSize.hasError()&& !ndefUsedSize.hasError() && arrayListForDisplay != null)
eventHandler.ReadNdef(convertByteArrayToHexString(tagId.getBytesData()), ndefMaxSize.getIntegerData(), ndefUsedSize.getIntegerData(), arrayListForDisplay);
}
}
private void sendNewTagFrame() {
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
boolean dataReadingError = false;
DataReply tagId = getTagId();
DataReply maxSize = getNdefMaxSize();
DataReply recordCount = getNdefRecordCount();
DataReply usedSize = getNdefUsedSize();
byte[][] records = new byte[recordCount.getIntegerData()][5];
for (int i = 0; i < recordCount.getIntegerData(); i++) {
DataReply recordTypeCategory = getRecordTypeCategory(i);
DataReply recordTypeLength = getNdefRecordTypeLength(i);
DataReply recordDataLength = getNdefRecordDataLength(i);
if (recordTypeCategory.hasError() || recordTypeLength.hasError() || recordDataLength.hasError()){
dataReadingError = true;
}else {
records[i][0] = (byte) recordTypeCategory.getIntegerData();
records[i][1] = (byte) recordTypeLength.getIntegerData();
records[i][2] = (byte) (recordTypeLength.getIntegerData() >> 8);
records[i][3] = (byte) recordDataLength.getIntegerData();
records[i][4] = (byte) (recordDataLength.getIntegerData() >> 8);
}
}
if (maxSize.hasError() || (recordCount.hasError() && recordCount.getError() != INDEX_OUT_OF_BOUNDS) || usedSize.hasError() || tagId.hasError() || records.length != recordCount.getIntegerData()){
dataReadingError = true;
}
if (!dataReadingError) {
ShieldFrame sf = new ShieldFrame(SHIELD_ID, NEW_TAG_FRAME);
sf.addArgument(tagId.getBytesData());
sf.addArgument(2, maxSize.getIntegerData());
sf.addArgument(1, recordCount.getIntegerData());
sf.addArgument(2, usedSize.getIntegerData());
for (int i = 0; i < recordCount.getIntegerData(); i++) {
sf.addArgument(records[i]);
}
sendShieldFrame(sf, true);
}else{
sendError(TAG_READING_ERROR);
}
}
}
}
private void sendNewEmptyTagFrame() {
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
DataReply tagId = getTagId();
DataReply maxSize = getNdefMaxSize();
if (!tagId.hasError() && !maxSize.hasError()) {
ShieldFrame sf = new ShieldFrame(SHIELD_ID, NEW_TAG_FRAME);
sf.addArgument(tagId.getBytesData());
sf.addArgument(2, maxSize.getIntegerData());
sf.addArgument(1, 0);
sf.addArgument(2, 0);
sendShieldFrame(sf, true);
}else {
sendError(TAG_READING_ERROR);
return;
}
}
}
}
private DataReply getTagId() {
DataReply dataReply = new DataReply();
byte[] currentTagId = new byte[0];
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
currentTagId = currentTag.getId();
if (currentTagId != null) {
if (currentTagId.length == 0)
currentTagId = new byte[]{0x00};
}else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
}
}else {
dataReply.setError(TAG_READING_ERROR);
}
dataReply.setBytesData(currentTagId);
return dataReply;
}
private DataReply getNdefUsedSize() {
DataReply dataReply = new DataReply();
int size = 0;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null && ndef.getCachedNdefMessage() != null)
size = ndef.getCachedNdefMessage().toByteArray().length;
else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
}
}
}
dataReply.setIntegerData(size);
return dataReply;
}
private DataReply getNdefMaxSize() {
DataReply dataReply = new DataReply();
int maxSize = 0;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
maxSize = ndef.getMaxSize();
if (maxSize <= 0){
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
}else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
dataReply.setIntegerData(maxSize);
return dataReply;
}
private DataReply getNdefRecordCount() {
DataReply dataReply = new DataReply();
int recordCount = 0;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
recordCount = ndef.getCachedNdefMessage().getRecords().length;
else
recordCount = 0;
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (recordCount > 256) {
dataReply.setError(INDEX_OUT_OF_BOUNDS);
recordCount = 256;
}
dataReply.setIntegerData(recordCount);
return dataReply;
}
private DataReply getRecordTypeCategory(int recordNumber) {
DataReply dataReply = new DataReply();
int type = UNKNOWN_TYPE;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
NdefRecord record = ndef.getCachedNdefMessage().getRecords()[recordNumber];
int tnfType = record.getTnf();
switch (tnfType) {
case NdefRecord.TNF_EMPTY:
type = EMPTY_TYPE;
break;
case NdefRecord.TNF_WELL_KNOWN:
if (Arrays.equals(record.getType(), NdefRecord.RTD_URI)) {
type = URI_TYPE;
} else if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT)) {
type = TEXT_TYPE;
} else {
type = UNSUPPORTED_TYPE;
}
break;
case NdefRecord.TNF_ABSOLUTE_URI:
type = ABSOLUTE_URI_TYPE;
break;
case NdefRecord.TNF_EXTERNAL_TYPE:
type = EXTERNAL_TYPE;
break;
case NdefRecord.TNF_MIME_MEDIA:
type = MIME_MEDIA_TYPE;
break;
case NdefRecord.TNF_UNCHANGED:
type = UNCHANGED_TYPE;
break;
case NdefRecord.TNF_UNKNOWN:
type = UNKNOWN_TYPE;
break;
default:
type = UNKNOWN_TYPE;
break;
}
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
dataReply.setIntegerData(type);
return dataReply;
}
private String getRecordTypeCategoryAsString(int recordNumber) {
DataReply typeInteger = getRecordTypeCategory(recordNumber);
String typeString = "UNKNOWN_TYPE";
if (!typeInteger.hasError())
switch (typeInteger.getIntegerData()) {
case UNKNOWN_TYPE:
typeString = "UNKNOWN_TYPE";
break;
case EMPTY_TYPE:
typeString = "EMPTY_TYPE";
break;
case EXTERNAL_TYPE:
typeString = "EXTERNAL_TYPE";
break;
case MIME_MEDIA_TYPE:
typeString = "MIME_MEDIA_TYPE";
break;
case UNCHANGED_TYPE:
typeString = "UNCHANGED_TYPE";
break;
case ABSOLUTE_URI_TYPE:
typeString = "ABSOLUTE_URI_TYPE";
break;
case TEXT_TYPE:
typeString = "TEXT_TYPE";
break;
case URI_TYPE:
typeString = "URI_TYPE";
break;
case UNSUPPORTED_TYPE:
typeString = "UNSUPPORTED_TYPE";
break;
}
return typeString;
}
private DataReply getNdefRecordTypeLength(int recordNumber) {
DataReply dataReply = new DataReply();
int length = 0;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
NdefRecord record = ndef.getCachedNdefMessage().getRecords()[recordNumber];
length = record.getType().length;
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
dataReply.setIntegerData(length);
return dataReply;
}
private DataReply getNdefRecordDataLength(int recordNumber) {
DataReply dataReply = new DataReply();
int length = 0;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
NdefRecord record = ndef.getCachedNdefMessage().getRecords()[recordNumber];
length = record.getPayload().length;
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
dataReply.setIntegerData(length);
return dataReply;
}
private DataReply readNdefRecordType(int recordNumber, int memoryIndex, int dataLength,int maxDataLength) {
DataReply dataReply = new DataReply();
String dataString = "";
if (dataLength > maxDataLength) dataLength = maxDataLength;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
dataString = new String(ndef.getCachedNdefMessage().getRecords()[recordNumber].getType());
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
byte[] data = dataString.getBytes();
if (memoryIndex < data.length && data.length > 0 && dataLength > 0) {
if (dataLength <= data.length - memoryIndex) {
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, memoryIndex + dataLength));
return dataReply;
} else {
dataReply.setError(NO_ENOUGH_BYTES);
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, data.length));
return dataReply;
}
} else {
dataReply.setError(INDEX_OUT_OF_BOUNDS);
return dataReply;
}
}
private DataReply readNdefRecordData(int recordNumber, int memoryIndex, int dataLength, int maxDataLength) {
DataReply dataReply = new DataReply();
String dataString = "";
if (dataLength > maxDataLength) dataLength = maxDataLength;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
dataString = new String(ndef.getCachedNdefMessage().getRecords()[recordNumber].getPayload());
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
byte[] data = dataString.getBytes();
if (memoryIndex < data.length && data.length > 0 && dataLength > 0) {
if (dataLength <= data.length - memoryIndex) {
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, memoryIndex + dataLength));
return dataReply;
} else {
dataReply.setError(NO_ENOUGH_BYTES);
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, data.length));
return dataReply;
}
} else {
dataReply.setError(INDEX_OUT_OF_BOUNDS);
return dataReply;
}
}
private DataReply readNdefRecordParsedData(int recordNumber, int memoryIndex, int dataLength, int maxDataLength) {
DataReply dataReply = new DataReply();
String dataString = "";
if (dataLength > maxDataLength) dataLength = maxDataLength;
if (currentTag != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD_MR1) {
Ndef ndef = Ndef.get(currentTag);
if (ndef != null) {
if (ndef.getCachedNdefMessage() != null)
if (ndef.getCachedNdefMessage().getRecords().length > recordNumber) {
NdefMessage message = ndef.getCachedNdefMessage();
try {
NdefRecord record = message.getRecords()[recordNumber];
short tnf = record.getTnf();
if (tnf == NdefRecord.TNF_WELL_KNOWN) {
String type;
if (Arrays.equals(record.getType(), NdefRecord.RTD_URI))
type = "Uri";
else if (Arrays.equals(record.getType(), NdefRecord.RTD_TEXT))
type = "Text";
else {
dataReply.setError(RECORD_CAN_NOT_BE_PARSED);
return dataReply;
}
if (type == "Text")
dataString = parseTextNdefRecord(record);
else if (type == "Uri") {
if (Integer.valueOf(record.getPayload()[0]) < UriTypes.length)
dataString = UriTypes[record.getPayload()[0]] + new String(record.getPayload()).substring(1);
else {
dataReply.setError(RECORD_CAN_NOT_BE_PARSED);
return dataReply;
}
} else {
dataReply.setError(RECORD_CAN_NOT_BE_PARSED);
return dataReply;
}
} else {
dataReply.setError(RECORD_CAN_NOT_BE_PARSED);
return dataReply;
}
} catch (UnsupportedEncodingException e) {
dataReply.setError(RECORD_CAN_NOT_BE_PARSED);
return dataReply;
//e.printStackTrace();
}
} else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
else {
dataReply.setError(RECORD_NOT_FOUND);
return dataReply;
}
} else {
dataReply.setError(TAG_READING_ERROR);
return dataReply;
}
try {
if (ndef != null) ndef.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
byte[] data = dataString.getBytes();
if (memoryIndex < data.length && data.length > 0 && dataLength > 0) {
if (dataLength <= data.length - memoryIndex) {
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, memoryIndex + dataLength));
return dataReply;
} else {
dataReply.setError(NO_ENOUGH_BYTES);
dataReply.setBytesData(Arrays.copyOfRange(data, memoryIndex, data.length));
return dataReply;
}
} else {
dataReply.setError(INDEX_OUT_OF_BOUNDS);
return dataReply;
}
}
private boolean getRecordParsableState(int recordNumber) {
DataReply recordTypeCategory = getRecordTypeCategory(recordNumber);
if (!recordTypeCategory.hasError()) {
if (recordTypeCategory.getIntegerData() == URI_TYPE | recordTypeCategory.getIntegerData() == TEXT_TYPE)
return true;
return false;
}else {
return false;
}
}
private String parseTextNdefRecord(NdefRecord ndefRecord) throws UnsupportedEncodingException {
byte[] payload = ndefRecord.getPayload();
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8":"UTF-16";
int languageCodeLength = payload[0] & 0063;
String plainData = new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
return plainData;
}
private String convertByteArrayToHexString(byte[] message) {
StringBuilder hexMessage = new StringBuilder();
for (byte b : message) {
hexMessage.append("0x" + String.format("%02X ", b));
}
return new String(hexMessage);
}
private String parsedPrintedText(String text) {
//replace all unprintable chars with printable one
for (int i = 0; i < 32; i++) {
text = text.replace((char) i, '\ufffd');
}
return text;
}
private void sendError(byte errorCode,boolean isQueued) {
ShieldFrame sf = new ShieldFrame(SHIELD_ID, TAG_ERROR_FRAME);
sf.addArgument(errorCode);
if(isQueued)
queueShieldFrame(sf);
else
sendShieldFrame(sf, true);
}
private void sendError(byte errorCode) {
sendError(errorCode,false);
}
private ArrayList<ArrayList<String>> generateArrayListForDisplay() {
ArrayList<ArrayList<String>> parentArrayList = new ArrayList<ArrayList<String>>();
ArrayList<String> childArrayList = new ArrayList<String>();
DataReply recordCount = getNdefRecordCount();
if (recordCount.getIntegerData() > 0 && (!recordCount.hasError() || recordCount.getError() == INDEX_OUT_OF_BOUNDS) ) {
for (int childCount = 0; childCount < recordCount.getIntegerData(); childCount++) {
String recordTypeCategory = getRecordTypeCategoryAsString(childCount);
DataReply ndefRecordTypeLength = getNdefRecordTypeLength(childCount);
DataReply ndefRecordType = readNdefRecordType(childCount, 0, ndefRecordTypeLength.getIntegerData(),ndefRecordTypeLength.getIntegerData());
DataReply ndefRecordDataLength = getNdefRecordDataLength(childCount);
DataReply ndefRecordData = readNdefRecordData(childCount, 0, ndefRecordDataLength.getIntegerData(),ndefRecordDataLength.getIntegerData());
boolean recordParsableState = getRecordParsableState(childCount);
DataReply ndefRecordParsedData = null;
if (recordParsableState) {
ndefRecordParsedData = readNdefRecordParsedData(childCount, 0, ndefRecordDataLength.getIntegerData(),ndefRecordDataLength.getIntegerData());
}
if (recordTypeCategory == null || ndefRecordTypeLength.hasError() || (ndefRecordType.hasError() && ndefRecordType.getError() != NO_ENOUGH_BYTES) || ndefRecordDataLength.hasError() || (ndefRecordData.hasError() && ndefRecordData.getError() != NO_ENOUGH_BYTES) || (recordParsableState && ndefRecordParsedData.hasError() && ndefRecordParsedData.getError() != NO_ENOUGH_BYTES)) {
return null;
}
childArrayList.add("Type Category: " + recordTypeCategory);
childArrayList.add("Type Size: " + String.valueOf(ndefRecordTypeLength.getIntegerData()));
childArrayList.add("Type Raw:\n" + convertByteArrayToHexString(ndefRecordType.getBytesData()));
childArrayList.add("Type:\n" + parsedPrintedText(new String(ndefRecordType.getBytesData())));
childArrayList.add("Data Size: " + String.valueOf(ndefRecordDataLength.getIntegerData()));
childArrayList.add("Data Raw:\n" + convertByteArrayToHexString(ndefRecordData.getBytesData()));
childArrayList.add("Data:\n" + parsedPrintedText(new String(ndefRecordData.getBytesData())));
if (recordParsableState && ndefRecordParsedData != null) {
childArrayList.add("Is Data Parsable: " + "true");
childArrayList.add("Parsed Data: " + parsedPrintedText(new String(ndefRecordParsedData.getBytesData())));
} else
childArrayList.add("Is Data Parsable: " + "false");
parentArrayList.add(childArrayList);
childArrayList = new ArrayList<String>();
}
return parentArrayList;
}else {
return null;
}
}
private class DataReply {
private byte error = 0;
private byte[] bytesData = new byte[0];
private int integerData = 0;
public boolean hasError(){
if (error == 0)
return false;
return true;
}
public void setError(byte error) {
this.error = error;
}
public void setIntegerData(int integerData) {
this.integerData = integerData;
}
public void setBytesData(byte[] bytesData) {
this.bytesData = bytesData;
}
public byte getError() {
return error;
}
public int getIntegerData() {
return integerData;
}
public byte[] getBytesData() {
return bytesData;
}
}
}