I have developped 2 Android applications. The first one, to write into an NFC tag, and the second to read the contents I have written .
This is what i did in the first application (WriteNFC)
private NdefRecord createRecord1(String data)
{
byte[] payload = data.getBytes(Charset.forName("UTF-8"));
byte[] empty = new byte[] {};
return new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, empty, empty, payload);
}
private NdefRecord createRecord2(String data)
{
byte[] payload = data.getBytes(Charset.forName("UTF-8"));
byte[] empty = new byte[] {};
return new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, payload, empty, empty);
}
And in the second application (ReadNFC)
NdefRecord cardRecord = msg.getRecords()[1];//Extract the second Record
String url_data = new String(cardRecord.getType());//Read data type
When I read with my own application (ReadNFC), of course I had on screen only the payload of the second Record, which I stored through "Record Type". But with a third-party application, especially that natively installed ("tag") -shown in photo-, It display correctly the first record, and for the second it's an empty field. How can I hide this field. Otherwise, how can I force the other third-party applications to not read the second record?
You simply cannot do that. Android will read the complete NDEF messages (i.e. all records) and pass it on in the Intent to an app.
Upps, it is no wonder this is happening, look at your code. First
return new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, empty, empty, payload);
then
return new NdefRecord(NdefRecord.TNF_ABSOLUTE_URI, payload, empty, empty);
So the 3rd party apps are really showing the correct data, you have a bug in your record creation.
The native NDEF support in android is somewhat crude (byte-array based), so I've written a library which helps in creating records - which you might find interesting. The above issue might be simple to resolve, but there are many other much more complex record types, so some help might come in handy ;-)
Edit: So if this is the preferred result, rather create an Unknown Record and put your 'secret' data as the payload - the will not be any good way for any 3rd party app to display that data - whereas the ID of the absolute URI Record certainly can be displayed by any NDEF-reading app (like mine?)
This third party app was bothersome to me, so I had to use foregroundDispatch to read the tag contents manually, there you have the freedom to read or not read anything you want.
This snippet is from the OnResume(). `
mNfcAdapter.enableForegroundDispatch(this, pendingIntent,
intentFiltersArray, techListsArray);
Toast.makeText(getApplicationContext(), "TAG disscovered",
Toast.LENGTH_LONG).show();
Parcelable[] rawMsgs = getIntent()
.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (rawMsgs != null) {
NdefMessage[] msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
setText=new String(msgs[i].getRecords()[0].getPayload());
}
mInfoText.setText(setText);
}
}`
Here I get payload of the 1st record.
Related
My Android app at the moment consumes a URL for a Google spreadsheet through pasting from the clipboard, reading a QR code, or reading from NFC. I'm having trouble writing to an NFC tag and I get this error:
[ERROR:nfa_rw_act.cc(1571)] Unable to write NDEF. Tag maxsize=137, request write size=171
I cannot write to this tag because the payload I'm trying to write it is larger than the writable space on it.
All I'm trying to do is write the URL I've already read (from clipboard or QR) to an NFC tag, and also add my app record so it launches the Play Store to my app in case the user doesn't have it installed already. Unfortunately, it seems this is too much data. I thought about maybe only including the spreadsheetID value, but I think this will add complications in the future when I inevitably want to add support for spreadsheets outside of Google Sheets.
Here's how I'm writing it currently:
public NdefMessage createNdefMessage() {
String text = "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit?usp=sharing";
NdefRecord appRecord = NdefRecord.createApplicationRecord(context.getPackageName());
NdefRecord relayRecord = new NdefRecord(NdefRecord.TNF_MIME_MEDIA, new String("application/" + context.getPackageName()).getBytes(Charset.forName("US-ASCII")), null, text.getBytes());
return new NdefMessage(new NdefRecord[] {relayRecord, appRecord});
}
Is writing just the ID ("1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms" in this case) my only option? I'm also assuming the NFC tag I'm using is an average size and not a super tiny one for 2018.
EDIT:
Thanks to Michael I was able to get it to fit, though barely (134/137 bytes). I write the URI to NFC via this:
NdefRecord relayRecord = NdefRecord.createUri(text);
I added this intent filter to catch it:
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="https" android:host="docs.google.com"/>
</intent-filter>
And I read the NFC tag with this:
NdefMessage[] messages = new NdefMessage[rawMessages.length];
for (int i = 0; i < rawMessages.length; i++) {
messages[i] = (NdefMessage) rawMessages[i];
}
for (NdefRecord r : messages[0].getRecords()) {
if (r.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
byte[] payload = r.getPayload();
try {
String payloadText = new String(payload, 1, payload.length - 1, "UTF-8");
int firstByte = payload[0];
return getUriPrefix(firstByte) + payloadText;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "Read error";
}
}
}
And the first byte from the URI compression I get via this, even though I only ever assign "04" (4 when I read it as int) in my app:
private String getUriPrefix(int firstByte) {
if (firstByte == 0) {
return "";
} else if (firstByte == 1) {
return "http://www.";
} else if (firstByte == 2) {
return "https://www.";
} else if (firstByte == 3) {
return "http://";
} else if (firstByte == 4) {
return "https://";
} else {
return "";
}
}
The error message that you got in the log is pretty clear. Your NDEF message is too large to fit the data area of the tag. The obvious solution would be to use NFC tags with a sufficiently large storage capacity -- there's quite a few larger tags available. There's nothing else you could do about it if you want to store exactly that NDEF message.
However, there is ways to improve the NDEF message itself. You currently use a MIME type record to store a URL. That's definitely not the best choice of a record type to store a URL. (or actually any application-specific data). The MIME type that you chose costs ("application/" + context.getPackageName()).length() = 31 bytes (assuming your package name is 19 bytes).
If you used an NFC Forum external type record instead, you could create a much shorter type name of the form "mydomain.tld:appurl", so this would save quite a few bytes.
relayRecord = NdefRecord.createExternal("mydomain.tld", "appurl", text.getBytes());
Since you want to store a URL, there's even a more efficient (and readily available) record type: the NFC Forum URI well-known type. The type name of that record consists only of the single letter "U", so this would already save 30 bytes compared to your MIME type record. Moreover, the URI record type uses a compression scheme to further reduce the size of the record: there is a list of well-known URI prefixes (e.g. "https://") that can be represented as a single byte in the URI record (the method NdefRecord.createUri() will automatically take care of that compression). Consequently, you would save another 7 bytes.
relayRecord = NdefRecord.createUri(text);
Good morning, I came across a problem that I do not understand, let me explain:
I'm working on a project based on microcontroller Arduino and an Android app.
The arduino recovers mails from pop3 commands and sends them to the Android application.
The two communicate through SMS only.
The problem is :
I get messages on Android via SMS Broadcaster to recover then a string (String).
Here is an example of mail recovered object:
Re: [Stage] techniques =?iso-8859-1?Q?vari?= =?iso-8859-1?B?6WVz?=de communication
This gives the following result:
Re: [Stage] techniques variées de communication
It may be noted that only some pieces of text are coded in ISO, in two different ways it seems.
My question is:
How can i parse the text ?
The problem is that using the SMS transmission I did not choose the type of return.
Here is the code I use to read a text message:
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[])bundle.get("pdus");
SmsMessage[] message = new SmsMessage[pdus.length];
String messageBody = null;
String phoneNumber = null;
for (int i=0; i< message.length;i++) {
message[i] = SmsMessage.createFromPdu((byte[])pdus[i]);
}
messageBody = message[0].getMessageBody();
phoneNumber = message[0].getDisplayOriginatingAddress();
if (message.length > -1) {
if (phoneNumber.equals(arduino)) {
Toast.makeText(context, "New Data",Toast.LENGTH_SHORT ).show();
//this.abortBroadcast();
Log.d("BroadcastSMS", "***************phoneNumber:"+phoneNumber+" messageBody:"+messageBody);
//TODO : ici, que faire du message "messageBody" pour l'interpréter.
}
}
}
I find myself already with a message as a String.
My instinct was to try to code myself a "shell" but I do not understand how are coded "ed" in this example.
Clarify my questions are:
What is the difference between 'Q' and 'B' in the tag ISO?
In my example the second tag should ISO code "ed" (note the space, if the words are glued). But I'm not in a case of correspondence between a hexadecimal code [0-F] [0-F] for the text is: 6WVz
But I may be on the wrong way,
if you have an answer to these questions or an other method can you help me ?
Thank you in advance anyway.
JM
It is RFC 2047 encoded-word. Q designates quoted-printable encoding while B is for base64.
I've started learning more about AAR and NFC and found this neat piece of code working. However, I have trouble understanding what it really does and is it already adding in the AAR into the NDEF message. Can someone guide me on what line of code does what? Thanks a lot!
private NdefMessage getTagAsNdef() {
boolean addAAR = false;
String uniqueId = "ichatime.com";
byte[] uriField = uniqueId.getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //add 1 for the URI Prefix
payload[0] = 0x01; //prefixes http://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload
NdefRecord rtdUriRecord = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
if(addAAR) {
// note: returns AAR for different app (nfcreadtag)
return new NdefMessage(new NdefRecord[] {
rtdUriRecord, NdefRecord.createApplicationRecord("com.example.ponpon")
});
} else {
return new NdefMessage(new NdefRecord[] {
rtdUriRecord});
}
}
An AAR is a form of External Type Record, for which you can download the spec from nfc-forum.org. An external type consists of three pieces of data:
Domain
Type
Value
I've written a library project which provides high-level NDEF record objects, which also includes external type records.
There is also a template for using NFC in activities you might be interested in :-)
I am asked to write the following data in smart poster tag.
1) URL of the discount offer
2) Count value (like offer for first 200 entries)
How to write these information in smart poster programmatically?
Is it something that can be written as key/value pairs?
Any pointers to sample code can help me a lot?
Thanks in advance.
I Think that that requirements 1) and 2), are two different levels.
Req. 1) is on the Android/NFC programming level, using TNF_WELL_KNOWN and RTD-SMART_POSTER(since this is a URL, usingRTD_URI works jus right).
here is some code:
private NdefRecord createRecord(String text)
throws UnsupportedEncodingException {
//Intent intent = getIntent();
//EditText editTextWeb = (EditText)
EditText editText = (EditText) findViewById(R.id.editTextWeblinks);
String webLink = editText.getText().toString();
byte[] uriField = webLink.getBytes(Charset.forName("US-ASCII"));
byte[] payload = new byte[uriField.length + 1]; //1 =URIPrefix
payload[0] = 0x01; //http://www. to the URI
System.arraycopy(uriField, 0, payload, 1, uriField.length); //appends URI to payload
NdefRecord rtdUriRecord = new NdefRecord(
NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
return rtdUriRecord;
}
On req. 2 we have a tricky part.
with Android/NFC API you can Identify each tag that you write but you can not add a sort of counter program logic within the NF Tag, this function have to be performed on an external application (name it on Android, PC, Mac, custom device, etc.).
Fancy way to do it: Build custom NFC terminal for sales and offers of your business.
Simpe way to do it: Build a simple app on the cashiers computers to scan a barcode (an image downloaded) and write a counter wi that event.
hope it helps.
I am writing an android app that recieves data over bluetooth. The bytes comming in can be of any size example: 00023>024935928598235>9284>
As you can see each set is seperated by ">". The data comes in extremely fast. I would like some ideas for an implementation. See my problem is that I need to read the data into a byte array that can and then convert it to a string and split them according to the delimeter of ">".
so in the above example:
00023
024935928598235
9284
If i set byte[] data = new byte[8] then when reading the incomming data it might get 00023>02 which is not what i want. I'm not sure how to implement something like this. Any ideas?
Here's one approach. You'll have to implement the readDataFromBluetooth() and somehow set dataAvailable, but this should get you on the right track.
byte[] data = new byte[1024];
List<String> chunks = new LinkedList<String>();
StringBuilder chunk = new StringBuilder();
while (dataAvailable) {
data = readDataFromBluetooth();
for (byte b : data) {
if (b == '<') {
chunks.add(chunk.toString());
chunk.setLength(0);
} else {
chunk.append(b);
}
}
}
if (chunk.length() > 0)
chunks.add(chunk.toString());
I would recommend using a buffered stream, but maybe a bit bigger that 8 bytes, as you suggest, and the read one and one character from the beginning of the stream, accumulating the string. When you encounter a ">", send the value you have accumulated off to a queue for a background thread processing. Use standard producer/consumer implementation techniques (e.g. the Monitor pattern) to communicate via the queue.