Question

I am trying to develop a use case such that when any user having a NFC enabled smartphone taps a NFC tag, the browser should open with the URL contained in the tag.

At present I am using a Mifare Classic 1K tag that contains the URL http://www.google.com that I have written in my NFC tag.

Now when I tap/scan the tag from my Google Nexus 7 (2012) edition, the tag gets detected but the browser isn't showing up. Also on other devices including Samsung S3, S4 the tag isnt detected at all. Why is that so?

Here is my code that does the writing and reading of tag,

   public class MainActivity extends Activity {

        @Override
       public void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.main);

              //check for nfc
          adapter = NfcAdapter.getDefaultAdapter(this);
          PendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
          IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
         tagDetected.addCategory(Intent.CATEGORY_DEFAULT);
         writeTagFilters = new IntentFilter[] { tagDetected }; 

                 //write tag
                 write("http://www.google.com",tag);

   }


 private void write(String text, Tag tag) throws IOException, FormatException {

    NdefRecord[] records = { createRecord(text) };
    NdefMessage  message = new NdefMessage(records);
    // Get an instance of Ndef for the tag.
    Log.i("App","Tag:" +tag);
    Ndef ndef = Ndef.get(tag);
    // Enable I/O
    ndef.connect();
    // Write the message
    ndef.writeNdefMessage(message);
    // Close the connection
    ndef.close();
}



   private NdefRecord createRecord(String text) throws UnsupportedEncodingException {
    String lang       = "en";
    byte[] textBytes  = text.getBytes();
    byte[] langBytes  = lang.getBytes("US-ASCII");
    int    langLength = langBytes.length;
    int    textLength = textBytes.length;
    byte[] payload    = new byte[1 + langLength + textLength];

    // set status byte (see NDEF spec for actual bits)
    payload[0] = (byte) langLength;

    // copy langbytes and textbytes into payload
    System.arraycopy(langBytes, 0, payload, 1,              langLength);
    System.arraycopy(textBytes, 0, payload, 1 + langLength, textLength);

    NdefRecord recordNFC = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,  NdefRecord.RTD_TEXT,  new byte[0], payload);

    return recordNFC;
}


@Override
protected void onNewIntent(Intent intent){
    if(NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())){
        mytag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        new NdefReaderTask().execute(mytag);
        Toast.makeText(this, this.getString(R.string.ok_detection) + mytag.toString(), Toast.LENGTH_LONG ).show();
    }
}


    private class NdefReaderTask extends AsyncTask<Tag, Void, String> {

    @Override
    protected String doInBackground(Tag... params) {
        Tag tag = params[0];

        Ndef ndef = Ndef.get(tag);
        if (ndef == null) {
            // NDEF is not supported by this Tag. 
            return null;
        }

        NdefMessage ndefMessage = ndef.getCachedNdefMessage();

        NdefRecord[] records = ndefMessage.getRecords();
        for (NdefRecord ndefRecord : records) {
            if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
                try {
                    return readText(ndefRecord);
                } catch (UnsupportedEncodingException e) {
                    Log.e("App", "Unsupported Encoding", e);
                }
            }
        }

        return null;
    }

    private String readText(NdefRecord record) throws UnsupportedEncodingException {
        /*
         * See NFC forum specification for "Text Record Type Definition" at 3.2.1 
         * 
         * http://www.nfc-forum.org/specs/
         * 
         * bit_7 defines encoding
         * bit_6 reserved for future use, must be 0
         * bit_5..0 length of IANA language code
         */

        byte[] payload = record.getPayload();

        // Get the Text Encoding
        String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";

        // Get the Language Code
        int languageCodeLength = payload[0] & 0063;

        // String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
        // e.g. "en"

        // Get the Text
        return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
    }

    @Override
    protected void onPostExecute(String result) {
        if (result != null) {

            Toast.makeText(getApplicationContext(), "Read content: " +result,Toast.LENGTH_LONG).show(); 
            webView = (WebView) findViewById(R.id.webView1);
            webView.getSettings().setJavaScriptEnabled(true);
            webView.loadUrl(result);
        }
    }
}

Manifest.xml

      <uses-permission android:name="android.permission.INTERNET" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name" >
    <activity
        android:label="@string/app_name"
        android:name="com.framentos.hellonfc.MainActivity" >
        <intent-filter >
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

</application>
<uses-permission android:name="android.permission.NFC" />

Also, do I need to have an app always present in the device for the browser to be invoked. As NFC is a service, shouldn't every NFC device open the browser on tap of tag.

Was it helpful?

Solution

That is because you are writing a text record with the text "http://www.google.com" to the tag. NFC devices usually do not know how to interpret text on tags (though they could just display that text -- and some devices do exactly that).

What you want to do is create a record based on the NFC Forum's URI Record Type Definition. The createRecord method could look like this:

private NdefRecord createRecord(String uri) throws UnsupportedEncodingException {
    byte[] uriBytes = uri.getBytes("UTF-8");
    byte[] payload = new byte[1 + uriBytes.length];

    // set prefix byte (see URI RTD for possibile values, we just use 0 indicating no prefix for now)
    payload[0] = 0;

    // copy uriBytes into payload
    System.arraycopy(uriBytes, 0, payload, 1, uriBytes.length);

    return new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
                          NdefRecord.RTD_URI,
                          null,
                          payload);
}

Or you could just use Android's built-in method:

record = NdefRecord.createUri("http://www.google.com");

Regarding the detection on other devices: Using a MIFARE Classic tag on the S3 should work. The S4 (and any other device based on Broadcom's NFC chipset) does not support MIFARE Classic at all, so you would need to switch to another tag technology to support those devices.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top