0

I have an app that can read a NDEF Tag, no big deal :-)

I want to read a NDEF tag multiple times and obviously to update the label accordingly. Imagine that my NDEF tag would contain the time (hh:mm:ss) and when I read it with my app, I want to see the time changing on my phone. It kind of works if I keep on position my phone over the tag on and off but I want to be able to auto-detect the NDEF Tag and as long as it's there, I want to read its content every x seconds.

I found this link : Write NDEF message multiple times to same tag? but it's about writing several times and I want to read.

I found this link : How to find out whether an NFC tag is still in range of an Android now? what's a start but I can't figure out how to update the label.

I am struggling and I even don't know if what I try doing is technically feasible.

Does anybody have some tips on how to do it? Cheers

More Info

This is my onCreate

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /// trying something
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
        exec.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                // do stuff
                Log.d(TAG, "i am printing ");
            }
        }, 0, 5, TimeUnit.SECONDS);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // here ask for TAG
                // read the TAG
                // and update the clock
            }
        }).start(); // Start the operation

        /////////////////

        mTextView = (TextView) findViewById(R.id.textView_explanation);

        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if (mNfcAdapter == null) {

            // Stop here, we definitely need NFC
            Toast.makeText(this, "This device doesn't support NFC.",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        if (!mNfcAdapter.isEnabled()) {
            mTextView.setText("NFC is currently disabled on this device.");
        }

        handleIntent(getIntent());
    }

and my handleIntent :

private void handleIntent(Intent intent) {
        String action = intent.getAction();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

            String type = intent.getType();
            if (MIME_TEXT_PLAIN.equals(type)) {

                Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
                new NdefReaderTask().execute(tag);

            } else {
                Log.d(TAG, "Wrong mime type: " + type);
            }
        } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {

            // In case we would still use the Tech Discovered Intent
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            String[] techList = tag.getTechList();
            String searchedTech = Ndef.class.getName();

            for (String tech : techList) {
                if (searchedTech.equals(tech)) {
                    new NdefReaderTask().execute(tag);
                    break;
                }
            }
        }
    }

Inside the ScheduledExecutorService, I added "Log.d(TAG, "i am printing ");", just to try to understand and it does works and prints regularly. What I don't get is if I ask for TAG, read the TAG and update the clock inside the new thread, what am I supposed to do inside the ScheduledExecutorService, and also how can I ask for TAG, read the TAG and update the clock inside the new thread, when it is currently done outside the onCreate method? For example, the result of the reading is posted in the textView inside my onPostExecute method.

Sorry, I have tried to watch some tutorials and examples but I still don't get it.

MORE INFO 2

This is my whole code. It takes a much longer time to read the tag and sometimes it doesn't. I can't figure out how and where to update the label, in case that would be the problem. Completely lost :-(

package uk.co.xxx.xxx;

import android.nfc.FormatException;
import android.support.v7.app.AppCompatActivity;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    public static final String MIME_TEXT_PLAIN = "text/plain";
    public static final String TAG = "NfcDemo";

    private TextView mTextView;
    private NfcAdapter mNfcAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /// trying something
        ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
        exec.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                // do stuff
                handleIntent(getIntent());
                Log.d(TAG, "i am printing ");
            }
        }, 0, 5, TimeUnit.SECONDS);
        /////////////////

        mTextView = (TextView) findViewById(R.id.textView_explanation);

        mNfcAdapter = NfcAdapter.getDefaultAdapter(this);

        if (mNfcAdapter == null) {

            Toast.makeText(this, "This device doesn't support NFC.",
                    Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        if (!mNfcAdapter.isEnabled()) {
            mTextView.setText("NFC is currently disabled on this device.");
        }

        handleIntent(getIntent());
    }



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

        /**
         * It's important, that the activity is in the foreground (resumed). Otherwise
         * an IllegalStateException is thrown.
         */
        setupForegroundDispatch(this, mNfcAdapter);
    }

    @Override
    protected void onPause() {
        /**
         * Call this before onPause, otherwise an IllegalArgumentException is thrown as well.
         */
        stopForegroundDispatch(this, mNfcAdapter);

        super.onPause();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        /**
         * This method gets called, when a new Intent gets associated with the current activity instance.
         * Instead of creating a new activity, onNewIntent will be called. For more information have a look
         * at the documentation.
         *
         * In our case this method gets called, when the user attaches a Tag to the device.
         */
        handleIntent(intent);
    }


    private void handleIntent(Intent intent) {
        String action = intent.getAction();
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {

            String type = intent.getType();
            if (MIME_TEXT_PLAIN.equals(type)) {

                Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
                new NdefReaderTask().execute(tag);


            } else {
                Log.d(TAG, "Wrong mime type: " + type);
            }
        } else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {

            // In case we would still use the Tech Discovered Intent
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
            String[] techList = tag.getTechList();
            String searchedTech = Ndef.class.getName();

            for (String tech : techList) {
                if (searchedTech.equals(tech)) {
                    new NdefReaderTask().execute(tag);
                    break;
                }
            }
        }
    }


    public static void setupForegroundDispatch(final Activity activity, NfcAdapter adapter) {
        final Intent intent = new Intent(activity.getApplicationContext(), activity.getClass());
        intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

        final PendingIntent pendingIntent = PendingIntent.getActivity(activity.getApplicationContext(), 0, intent, 0);

        IntentFilter[] filters = new IntentFilter[1];
        String[][] techList = new String[][]{};


        filters[0] = new IntentFilter();
        filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
        filters[0].addCategory(Intent.CATEGORY_DEFAULT);
        try {
            filters[0].addDataType(MIME_TEXT_PLAIN);
        } catch (MalformedMimeTypeException e) {
            throw new RuntimeException("Check your mime type.");
        }

        adapter.enableForegroundDispatch(activity, pendingIntent, filters, techList);
    }


    public static void stopForegroundDispatch(final Activity activity, NfcAdapter adapter) {
        adapter.disableForegroundDispatch(activity);
    }

    /**
     * Background task for reading the data. Do not block the UI thread while reading.
     */

    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(TAG, "Unsupported Encoding", e);
                    }
                }
            }

            return null;
        }

        private String readText(NdefRecord record) throws UnsupportedEncodingException {

            byte[] payload = record.getPayload();

            // Get the Text Encoding

            String textEncoding = new String ("");
            if ((payload[0] & 128) == 0) {
                textEncoding = "UTF-8";
            } else {
                textEncoding = "UTF-16";
            }

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


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

        @Override
        protected void onPostExecute(String result) {
            if (result != null) {
                mTextView.setText("The time is: \n" + result);
            }
        }
    }
}
Community
  • 1
  • 1
Clararhea
  • 159
  • 1
  • 2
  • 13
  • look into `EXTRA_READER_PRESENCE_CHECK_DELAY` from the `NfcAdapter` – Pim Apr 25 '16 at 18:28
  • @Pim I am not sure I understand. I read about it but isn't it doing the opposite of what I want to do like delaying the recognition of the tag whereas what I want is to keep on seeing it? May be I misunderstood what I read. Would you explain a bit more please? – Clararhea Apr 25 '16 at 20:35

1 Answers1

0

With TAG = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); you can get the TAG. Now, you can check the TAG every x seconds, and if it is not null (or it have last detected type) you read the tag and update the clock (I suppose it is a text view). You can do this into a Thread like:

new Thread(new Runnable() {
  @Override
  public void run() {
  // here ask for TAG
  // read the TAG
  // and update the clock             
  }
}).start(); // Start the operation

I hope I understood correctly your question.

Mihai Coman
  • 73
  • 1
  • 9
  • Thanks for your answer. I think I understand what you mean and how to do it, but before I try, (I am not that good with Android) : do I put this new Thread anywhere in my MainActivity? I have never created a new thread before. – Clararhea Apr 26 '16 at 08:17
  • To refine my answer, you can use this method: [link](http://stackoverflow.com/questions/3541676/java-thread-every-x-seconds). you simply copy the code from the first answer anywhere in onCreate method from MainActivity. Will be started this thread every x seconds. – Mihai Coman Apr 26 '16 at 08:21
  • I have edited my initial question with more info to show some stuff I still don't get – Clararhea Apr 26 '16 at 12:57
  • Where you print (inside the run method from the first Thread) you ask for the tag with `mNfcAdapter = NfcAdapter.getDefaultAdapter(this);` or `TAG = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);` . The second Thread you can delete it. – Mihai Coman Apr 26 '16 at 17:28
  • So, it will be something like: `/// trying something ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor(); exec.scheduleAtFixedRate(new Runnable() { @Override public void run() { intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); // ask for tag, read it and update the clock Log.d(TAG, "i am printing "); } }, 0, 5, TimeUnit.SECONDS);` – Mihai Coman Apr 26 '16 at 17:32
  • I tried something like this during the day, but I always end up with the error " Cannot resolve symbole 'intent'" – Clararhea Apr 26 '16 at 18:10
  • Sorry for my stupidity. The `intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);` can be used only into the intent handle. Please try to call `handleIntent(getIntent());` – Mihai Coman Apr 27 '16 at 05:09
  • I have added a MORE INFO 2 to my initial post. – Clararhea Apr 27 '16 at 08:18