Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple scans/intents delivered to receiving Activity

I have an ongoing problem where my app is delivering multiple intents to the recieving Activity after an NFC tag scan.

My App essentially scans an NFC tag which has some data on it and sends it to the server. In general it works fine. I understand that NFC works within a threshold of a few cms and perhaps my problem is user error, for example the user is waving the phone around the tag which could scan it twice within millisecs.

I have put a timer in the Application object. When the user scans a tag, i start the timer going and set a flag to false, this flag is checked within the Activity that handles the NFC intent and the the intent is dropped if the flag is false. This (should) stop multiple scans of a tag within 10 secs.

My app is used for HomeCare work, so a user will scan a tag in the clients house. When they scan the tag the first time it logs them in at that client with data and a time stamp. The second scan of the tag will log them out.

In general it works but sometimes a carer will log in and out at the first client, then at the second client or sometimes hours later, the carer will be logged back in at the previous client.

What i think is happening is the carer is mis-scanning the tag which is triggering the NFC adpter twice. The first intent is delivered straight away, then the second is delivered minutes/hours later.

How things flow. scan tag -> intent delivered to Activity -> start AppObj timer. write data to sqlite DB -> start an IntentService to send the transaction to server(cancelled after 10 seconds).

Can anyone see a problem i've missed that could account for why a duplicate Intent is delivered hours later?

Can anyone suggest a solution which would stop this happening?

Is there a problem whith how the intent is handled in the activity? eg the wrong code in the wrong Activity lifecycle method?

Is there a way to cancel/intercept the second intent from the second mis-scan?

Basically, what i want is that the nfcAdapter could be triggered 10 times in one second, but only the first scan is delivered to the activity. How can i achieve this?

i'll post some code.

[edit2]

Toast.makeText(this, "about to test flags", Toast.LENGTH_LONG).show();

        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {

            Toast.makeText(this, "intent is from history", Toast.LENGTH_LONG).show();

        }else{

            Toast.makeText(this, "intent is not from history", Toast.LENGTH_LONG).show();

        }

.

[edit3]

<activity
            android:name=".NfcscannerActivity"
            android:screenOrientation="portrait"

            >
            <intent-filter>
                <action android:name="android.nfc.action.NDEF_DISCOVERED" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="text/plain" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.carefreegroup.rr3.QRCODE_ACTION" />

                <category android:name="android.intent.category.DEFAULT" />

                <data android:mimeType="text/plain" />
            </intent-filter>

            <intent-filter>
                <action android:name="android.nfc.action.TECH_DISCOVERED" />

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

            <meta-data
                android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />

        </activity>

        <activity
            android:name=".EntryActivity"
            android:label="@string/app_name"
            android:screenOrientation="portrait"
            android:launchMode="singleTask" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

            <intent-filter>
                <action android:name="com.carefreegroup.rr3.INVALID_CARER_TAG_SCANNED" />

                <category android:name="android.intent.category.DEFAULT" />


            </intent-filter>
        </activity>

. [edit4]

Tag tagTest = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);

            Ndef ndefTag = Ndef.get(tagTest);

            try {
                Log.e(TAG, "about to test io operations on Ndef Tag");
                ndefTag.connect();  // this should already perform an IO operation and should therefore fail if there is no tag
                NdefMessage ndefMsg = ndefTag.getNdefMessage(); // this reads the current NDEF message from the tag and consequently causes an IO operation
                Log.e(TAG, "tested io operations on Ndef Tag, must be a real Tag!!!!!******!!!!!!");
            } catch (Exception e) {
                // there is no tag or communication with tag dropped
                Log.e(TAG, "tested io operations on Ndef Tag, No Tag there, must be a re-delivery of the old Tag via an intent!!!!!******!!!!!!!!!!!!!!!!!!!!!!");
                onResume();
            } finally {
                try {
                    ndefTag.close();
                } catch (Exception e) {
                }
            }
like image 471
turtleboy Avatar asked Apr 29 '14 10:04

turtleboy


1 Answers

From what you describe, I would assume that your affected users "close" (actually hide) the app by pressing the home key. Later they are opening the app (or rather that activity) from the history (long-press home key).

Assuming that you process the NFC-related intents only in onCreate(), you would normally not run into the issue you described when starting your activity from the history as the onCreate() method would not be called if your activity still existed in the background. But if your activity has been destroyed meanwhile (e.g. app has been manually killed, app has been killed by the system to free resources, etc), re-launching the app from the history will cause your onCreate() method to be run again. As Android rebuilds the activity stack and re-sends the original intents (i.e. the intent that initially launched your activity) again when opening apps from the history, you activity will receive the NFC intent that it had received when it was initially started.

You can overcome this problem by checking if the intent you receive has the FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY set. This would be the case only if your activity was started from the history.

if ((intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
     if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction()) ||
         NfcAdapter.ACTION_TECH_DISCOVERED.equals(intent.getAction())) {
        ...
    }
}

Not solving your initial problem but as a last resort, remember that NFC tags can be accessed by IO operations after receiving an intent. So in order to check if the tag you receive in the intent actually exists, you could perform such an IO operation.

If, for instance, your tag is an NDEF tag:

Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
Ndef ndefTag = Ndef.get(tag);

try {
    ndefTag.connect();  // this should already perform an IO operation and should therefore fail if there is no tag
    NdefMessage ndefMsg = ndefTag.getNdefMessage();  // this reads the current NDEF message from the tag and consequently causes an IO operation
} catch (Exception e) {
    // there is no tag or communication with tag dropped
} finally {
    try {
        ndefTag.close();
    } catch (Exception e) {
    }
}

For non-NDEF tags, you would need to know how to access the tag using low-level commands though. But at least the connect test would work for any tag.

like image 110
Michael Roland Avatar answered Sep 21 '22 13:09

Michael Roland