Why Android NFC reader add's "en" before the message?
Asked Answered
A

3

6

Hey guys im testing NFC and i have an issue with i think it is formating..

I added a string value in the tag with an app to write.

when i scan it with that same store app it shows ok...

when i scan with my app it shows the correct name in the app but adds a "en" before the message..

and a white space.. so if i add a name in the tag then scan and get a API it shows 404 cause there is an en and white space..

http://myapisite.com/API/getdevice.php?id= enTagString

before the = space and then en before the id or even name i put..

i tried a few ways..

public class MainActivity extends Activity {
    // list of NFC technologies detected:
    private final String[][] techList = new String[][]{
            new String[]{
                    NfcA.class.getName (),
                    NfcB.class.getName (),
                    NfcF.class.getName (),
                    NfcV.class.getName (),
                    IsoDep.class.getName (),
                    MifareClassic.class.getName (),
                    MifareUltralight.class.getName (), Ndef.class.getName ()
            }
    };
    private TextView mTextView;
    private ImageView mImageView;
    private String ID, machineName, MachineImg, MachinePart, level;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView (R.layout.activity_main);
        mImageView = findViewById (R.id.imageView);
        mTextView = findViewById (R.id.textView_explanation);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater ().inflate (R.menu.menu, menu);
        return true;
    }

    @Override
    protected void onResume() {
        super.onResume ();
        PendingIntent pendingIntent = PendingIntent.getActivity (this, 0, new Intent (this, getClass ()).addFlags (Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        // creating intent receiver for NFC events:
        IntentFilter filter = new IntentFilter ();
        filter.addAction (NfcAdapter.ACTION_TAG_DISCOVERED);
        filter.addAction (NfcAdapter.ACTION_NDEF_DISCOVERED);
        filter.addAction (NfcAdapter.ACTION_TECH_DISCOVERED);
        // enabling foreground dispatch for getting intent from NFC event:
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter (this);
        nfcAdapter.enableForegroundDispatch (this, pendingIntent, new IntentFilter[]{filter}, this.techList);
    }

    @Override
    protected void onPause() {
        super.onPause ();
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter (this);
        nfcAdapter.disableForegroundDispatch (this);
    }

    void parseNdefMessage(Intent intent) {
        Parcelable[] ndefMessageArray = intent.getParcelableArrayExtra (
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        NdefMessage ndefMessage = (NdefMessage) ndefMessageArray[0];
        String text = new String (ndefMessage.getRecords ()[0].getPayload ());
        Log.d (TAG, "PAYLOAD MESS" + text);
        ID = text;
        getApiInfos ();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        parseNdefMessage (intent);
        if (intent.getAction ().equals (NfcAdapter.ACTION_TAG_DISCOVERED)) {
            mTextView.setText ("NFC Tag\n" + ByteArrayToHexString (intent.getByteArrayExtra (NfcAdapter.EXTRA_ID)));

            Parcelable tagN = intent.getParcelableExtra (NfcAdapter.EXTRA_TAG);
            if (tagN != null) {
                NdefMessage[] msgs;
                byte[] empty = new byte[0];
                byte[] id = intent.getByteArrayExtra (NfcAdapter.EXTRA_ID);
                byte[] payload = dumpTagData (tagN).getBytes ();
                NdefRecord record = new NdefRecord (NdefRecord.TNF_UNKNOWN, empty, id, payload);
                NdefMessage msg = new NdefMessage (new NdefRecord[]{record});
                msgs = new NdefMessage[]{msg};
                Log.d (TAG, msgs[0].toString ());

            } else {
                Log.d (TAG, "Parcelable NULL");
            }
            Parcelable[] messages1 = intent.getParcelableArrayExtra (NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (messages1 != null) {
                Log.d (TAG, "Found " + messages1.length + " NDEF messages");
                for (int i = 0; i < messages1.length; ++i) {
                    Log.d (TAG, "Found M " + messages1[i].toString ());
                }
            } else {
                Log.d (TAG, "Not EXTRA_NDEF_MESSAGES");
            }

            Tag tag = intent.getParcelableExtra (NfcAdapter.EXTRA_TAG);
            Ndef ndef = Ndef.get (tag);
            if (ndef != null) {
                Parcelable[] messages = intent.getParcelableArrayExtra (NfcAdapter.EXTRA_NDEF_MESSAGES);
                if (messages != null) {
                    Log.d (TAG, "Found " + messages.length + " NDEF messages");
                }
            } else {
                Log.d (TAG, "Write to an unformatted tag not implemented");
            }
        }
    }

    private String dumpTagData(Parcelable p) {
        StringBuilder sb = new StringBuilder ();
        Tag tag = (Tag) p;
        byte[] id = tag.getId ();
        sb.append ("Tag ID (hex): ").append (getHex (id)).append ("\n");
        sb.append ("Tag ID (dec): ").append (getDec (id)).append ("\n");
        sb.append ("ID (reversed): ").append (getReversed (id)).append ("\n");
        String prefix = "android.nfc.tech.";
        sb.append ("Technologies: ");
        for (String tech : tag.getTechList ()) {
            sb.append (tech.substring (prefix.length ()));
            sb.append (", ");
        }
        sb.delete (sb.length () - 2, sb.length ());
        for (String tech : tag.getTechList ()) {
            if (tech.equals (MifareClassic.class.getName ())) {
                sb.append ('\n');
                MifareClassic mifareTag = MifareClassic.get (tag);
                String type = "Unknown";
                switch (mifareTag.getType ()) {
                    case MifareClassic.TYPE_CLASSIC:
                        type = "Classic";
                        break;
                    case MifareClassic.TYPE_PLUS:
                        type = "Plus";
                        break;
                    case MifareClassic.TYPE_PRO:
                        type = "Pro";
                        break;
                }
                sb.append ("Mifare Classic type: ");
                sb.append (type);
                sb.append ('\n');

                sb.append ("Mifare size: ");
                sb.append (mifareTag.getSize () + " bytes");
                sb.append ('\n');

                sb.append ("Mifare sectors: ");
                sb.append (mifareTag.getSectorCount ());
                sb.append ('\n');

                sb.append ("Mifare blocks: ");
                sb.append (mifareTag.getBlockCount ());
            }

            if (tech.equals (MifareUltralight.class.getName ())) {
                sb.append ('\n');
                MifareUltralight mifareUlTag = MifareUltralight.get (tag);
                String type = "Unknown";
                switch (mifareUlTag.getType ()) {
                    case MifareUltralight.TYPE_ULTRALIGHT:
                        type = "Ultralight";
                        break;
                    case MifareUltralight.TYPE_ULTRALIGHT_C:
                        type = "Ultralight C";
                        break;
                }
                sb.append ("Mifare Ultralight type: ");
                sb.append (type);
            }
        }

        DateFormat TIME_FORMAT = SimpleDateFormat.getDateTimeInstance ();
        Date now = new Date ();

        mTextView.setText (TIME_FORMAT.format (now) + '\n' + sb.toString ());
        return sb.toString ();
    }


    private String getHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder ();
        for (int i = bytes.length - 1; i >= 0; --i) {
            int b = bytes[i] & 0xff;
            if (b < 0x10)
                sb.append ('0');
            sb.append (Integer.toHexString (b));
            if (i > 0) {
                sb.append (" ");
            }
        }
        return sb.toString ();
    }

    private long getDec(byte[] bytes) {
        long result = 0;
        long factor = 1;
        for (int i = 0; i < bytes.length; ++i) {
            long value = bytes[i] & 0xffl;
            result += value * factor;
            factor *= 256l;
        }
        return result;
    }

    private long getReversed(byte[] bytes) {
        long result = 0;
        long factor = 1;
        for (int i = bytes.length - 1; i >= 0; --i) {
            long value = bytes[i] & 0xffl;
            result += value * factor;
            factor *= 256l;
        }
        return result;
    }

    private String ByteArrayToHexString(byte[] inarray) {
        int i, j, in;
        String[] hex = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
        String out = "";

        for (j = 0; j < inarray.length; ++j) {
            in = (int) inarray[j] & 0xff;
            i = (in >> 4) & 0x0f;
            out += hex[i];
            i = in & 0x0f;
            out += hex[i];
        }
        Log.d ("ByteArrayToHexString", String.format ("%0" + (inarray.length * 2) + "X", new BigInteger (1, inarray)));
        return out;
    }
enter code here
Almita answered 28/12, 2019 at 22:7 Comment(2)
What App are you using to write the string?Lafountain
im using nfc writer...but i tried a few...same issue..Almita
L
11

Most likely this is because the App writing is storing it as a "WELL_KNOWN" type of text/plain

The specs for NFC Text format can be found at NFCForum-TS-RTD_Text_1.0.pdf.

The extra characters in the payload are as below:

  • language length (1 byte) + language (n bytes) + Text

So the space is the actually a non printable size of the language.

The "en" is defining that the text is English.

https://developer.android.com/reference/android/nfc/NdefRecord#getPayload() returns a byte array.

So below should work to trim it (tested) as there don't seem to be nice helper methods to decode it, only create this format.

// Create Test Record
NdefRecord record = NdefRecord.createTextRecord("en", "Hello");

//Get Bytes of payload
// byte[] payload = ndefMessage.getRecords ()[0].getPayload ();
// Get Bytes of test NDEF Record
byte[] payload = record.getPayload ();
// Read First Byte and then trim off the right length
byte[] textArray = Arrays.copyOfRange(payload, (int) payload[0] + 1 , payload.length);
// Convert to Text
String text = new String(textArray);

UPDATE: As the code given in the question is a bit of a mess and you do things you don't need to do and don't do things you do need to do (e.g. you have asked to be sent data from cards that won't have NDef data in them and then you parse for Ndef data without checking there is some data to parse)

I've simplified the code just to read a text NDEF record (Note not tested, but based on how I used to read NDEF data, I now use a better method to read cards that is more reliable especially if you want to write to cards as well)

public class MainActivity extends Activity {
    private TextView mTextView;
    private ImageView mImageView;
    private String ID, machineName, MachineImg, MachinePart, level;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mImageView = findViewById(R.id.imageView);
        mTextView = findViewById(R.id.textView_explanation);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    protected void onResume() {
        super.onResume();
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
        // creating intent receiver for NFC events:
        IntentFilter filter = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        // enabling foreground dispatch for getting intent from NFC event:
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        nfcAdapter.enableForegroundDispatch(this, pendingIntent, new IntentFilter[]{filter}, null);
    }

    @Override
    protected void onPause() {
        super.onPause();
        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
        nfcAdapter.disableForegroundDispatch(this);
    }

    void parseNdefMessage(Intent intent) {
        Parcelable[] ndefMessageArray = intent.getParcelableArrayExtra(
                NfcAdapter.EXTRA_NDEF_MESSAGES);
        // Test if there is actually a NDef message passed via the Intent
        if (ndefMessageArray != null) {
            NdefMessage ndefMessage = (NdefMessage) ndefMessageArray[0];
            //Get Bytes of payload
            byte[] payload = ndefMessage.getRecords()[0].getPayload();
            // Read First Byte and then trim off the right length
            byte[] textArray = Arrays.copyOfRange(payload, (int) payload[0] + 1, payload.length);
            // Convert to Text
            String text = new String(textArray);
            ID = text;
            getApiInfos();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {

        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            parseNdefMessage(intent);
        }
    }
}
Lafountain answered 29/12, 2019 at 0:2 Comment(6)
hmm..ok is there a way to trim that? and the en also?Almita
There are many ways to trim it, read the first byte in the array and then use that to work out the first byte of the actual textLafountain
if i use the code of Andrew i get 6 32 36 2d 04 from there how do i decode that?Almita
Sorry there was a Typo in the copyOfRange line, now fixed and added an NdefRecord to show how it was tested.Lafountain
seems to work, but i only get the hello, how do i get the tag msg? in my parsendefmessage if i add NdefRecord record = NdefRecord.createTextRecord ("en", new String (ndefMessage.getRecords ()[0].getPayload ())); i still get en infront of messageAlmita
Don't wrap you existing NDef Record in a new NDef Text Record, that was from testing only to show the parsing worked, you should just use the commented out line byte[] payload = ndefMessage.getRecords ()[0].getPayload (); to get the first record payload that you from the NDef Message you got from the card.Lafountain
A
0

Ok thx to @Andrew i used part of your code and adapted it to my tests.. it works great now i can clean it up and removed what is not used or needed.Thx happy holidays!:)

void parseNdefMessage(Intent intent) {
    Parcelable[] ndefMessageArray = intent.getParcelableArrayExtra(
            NfcAdapter.EXTRA_NDEF_MESSAGES);
    // Test if there is actually a NDef message passed via the Intent
    if (ndefMessageArray != null) {
        NdefMessage ndefMessage = (NdefMessage) ndefMessageArray[0];
        //Get Bytes of payload
        byte[] payload = ndefMessage.getRecords()[0].getPayload();
        // Read First Byte and then trim off the right length
        byte[] textArray = Arrays.copyOfRange(payload, (int) payload[0] + 1, payload.length);
        // Convert to Text
        String text = new String(textArray);
        ID = text;
        getApiInfos();
    }
}
Almita answered 29/12, 2019 at 17:33 Comment(0)
F
0

If anyone is facing the same issue in React Native, here's my solution. Here, the react-native-nfc-manager package is used

      // register for the NFC tag with NDEF in it
      await NfcManager.requestTechnology(NfcTech.Ndef);
      // the resolved tag object will contain `ndefMessage` property
      const tag = await NfcManager.getTag();
      // access the payload
      const payload = tag?.ndefMessage[0].payload;
      //remove the language code from payload
      const subPayload = payload?.slice(3, payload.length);
      const decoded = Ndef.util.bytesToString(subPayload);
Female answered 23/12, 2022 at 5:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.