Strange character when reading NFC tag
Asked Answered
Q

1

5

I am trying to read an NFC tag using Android. I'm a beekeeper and this is to ID my hives when I approach them. I have searched here but I am still having issues reading the tag. I want to read the text, but when it reads, there is a square-like character and characters displayed like " Ten" before the desired text.

Here is the code I'm using. I know that the payload bytes have to be correct and I have tried changing them but to no avail.

private static NdefMessage getTestMessage() {
    byte[] mimeBytes = "application/com.android.cts.verifier.nfc"
            .getBytes(Charset.forName("US-ASCII"));
    byte[] id = new byte[] {1, 3, 3, 7};
    byte[] payload = "CTS Verifier NDEF Push Tag".getBytes(Charset.forName("US-ASCII"));
    return new NdefMessage(new NdefRecord[] {
            new NdefRecord(NdefRecord.TNF_MIME_MEDIA, mimeBytes, id, payload)
    });
}

@Override
protected void onResume() {
    super.onResume();

    mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
    mNfcAdapter.setNdefPushMessageCallback(this, this);
}

// sending message
@Override
public NdefMessage createNdefMessage(NfcEvent event) {
    return getTestMessage();
}


private NdefMessage[] getNdefMessages(Intent intent) {
    Parcelable[] rawMessages = intent
      .getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
    if (rawMessages != null) {
        NdefMessage[] messages = new NdefMessage[rawMessages.length];
        for (int i = 0; i < messages.length; i++) {
            messages[i] = (NdefMessage) rawMessages[i];
        }
        return messages;
    } else {
        return null;
    }
}

static String displayByteArray(byte[] bytes) {
    String res="";
    StringBuilder builder = new StringBuilder().append("");
    for (int i = 0; i < bytes.length; i++) {
        res+=(char)bytes[i];
    }
    return res;

}

// displaying message
@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);

    NdefMessage[] messages = getNdefMessages(intent);
    edtUser.setText(displayByteArray(messages[0].toByteArray()));

    Toast.makeText(this, "NFC tag entered", Toast.LENGTH_LONG).show();
}
Quatrefoil answered 15/4, 2018 at 12:1 Comment(0)
G
5

You are getting odd additional characters when displaying the message since you try to display the whole raw NDEF record as text string (using some odd decoding method):

NdefMessage[] messages = getNdefMessages(intent);
edtUser.setText(displayByteArray(messages[0].toByteArray()));

There are several problems with this. First of all, you would typically want to decode the text using the same encoding that you used to write the text. For instance, if you used

String text = "...";
byte[] bytes = text.getBytes(Charset.forName("US-ASCII"));

to get a byte array in US-ASCII encoding for a given text string, you would also want to use that same US-ASCII encoding to translate the bytes into a text string again:

byte[] bytes = ...;
String text = new String(bytes, "US-ASCII");

Second, you are interpreting the whole NDEF message as a text string. However, the text that you stored on the tag is typically only contained inside the payload of an NDEF record. In your case, the prefix "Ten" suggests that you used an NFC Forum Text record (type name "T") with the language indication "en" (for English). You would, therefore, want to search the NDEF message for the Text record:

for (NdefRecord r : messages[0].getRecords()) {
    if (r.getTnf() == NdefRecord.TNF_WELL_KNOWN) {
        if (Arrays.equals(r.getType(), NdefRecord.RTD_TEXT)) {

Once you found the Text record, you can decode its text payload. The payload consists of a status byte, a language field and the actual text:

            byte[] payloadBytes = ndefRecord.getPayload();
            boolean isUTF8 = (payloadBytes[0] & 0x080) == 0;  //status byte: bit 7 indicates encoding (0 = UTF-8, 1 = UTF-16)
            int languageLength = payloadBytes[0] & 0x03F;     //status byte: bits 5..0 indicate length of language code
            int textLength = payloadBytes.length - 1 - languageLength;
            String languageCode = new String(payloadBytes, 1, languageLength, "US-ASCII");
            String payloadText = new String(payloadBytes, 1 + languageLength, textLength, isUTF8 ? "UTF-8" : "UTF-16");

            edtUser.setText(payloadText);
        }
    }
}
Giesser answered 29/4, 2018 at 9:30 Comment(4)
Your String languageCode isn't used in your code snippet. What is it's purpose here?Dishman
@ZeekAran It's an IANA language code (acutally IETF language tag) that indictes the language that the text was written in.Giesser
Sure, but it's not being referenced again in the code.Dishman
@ZeekAran Correct, my example shows how to properly decode a Text record. The OP did not seem to care about that element in their code. Hence, I did not "use" it in my example either.Giesser

© 2022 - 2024 — McMap. All rights reserved.