Closed homer-jay closed 6 years ago
I couldn't determine the cause of the issue. Seems to be not related to Cordova versions, and apparently it can be observed across devices of different brands and running different OS versions. Interestingly, with the same type of tags and devices used for the tests above exposed, this problem does not happen when calling addTagDiscoveredListener
.
The intent processed by the parseMessage
method in NfcPlugin.java contains the ACTION_NDEF_DISCOVERED
action only the very first time a non MIME NDEF tag is read. If this intent comes from the OS, then this is a bug in Android source, since it shouldn't be firing this Intent for non MIME records. From the Android NFC Basics Guide:
ACTION_TECH_DISCOVERED: If no activities register to handle the ACTION_NDEF_DISCOVERED intent, the tag dispatch system tries to start an application with this intent. This intent is also directly started (without starting ACTION_NDEF_DISCOVERED first) if the tag that is scanned contains NDEF data that cannot be mapped to a MIME type or URI, or if the tag does not contain NDEF data but is of a known tag technology.
In successive reads over the same tag, the intent contains the ACTION_TECH_DISCOVERED action as expected. Which is absurd since the tag is the same.
A workaround is proposed in this pull request. The NfcPlugin.java should also fire the more general "ndef" event so that the callbacks registered with addNdefListener
are notified.
@homer-jay thanks for the detailed bug report. I'll go through these steps and try to reproduce when I'm back in the office. Other people have mentioned this issue before, but I have not been able to duplicate it. Unfortunately, I think that it is a hardware specific issue. I have a Samsung Galaxy S3 test device, so hopefully it behaves like your S3 mini.
For now the best work around is to add 2 listeners in JavaScript. Android will call the most specific callback for the tag scanned, so you will not get duplicate callbacks when you have multiple listeners. Use the same callback for both listeners. It doesn't matter what mime type you add.
var onNfc = function(nfcEvent) {
console.log("Tag detected");
};
nfc.addNdefListener(onNfc, success, failure);
// 2nd listener for issue #217 on Samsung devices
nfc.addMimeTypeListener("text/bogus", onNfc, success, failure);
An alternate solution could be to wire the ndef-mime event to your ndef event handler
document.addEventListener("ndef-mime", onNfc, false);
You are welcome. I think this might be an Android bug rather than a HW issue. I've tested in so many devices, some of them really old, and I think they have different chipsets.
BTW, I created the tag using NXP's TagWriter (the last version). You can create exactly the same tag by selecting Write tags -> New dataset -> Plain text.
I think I've finally tracked this one down. When the plugin starts it calls nfcAdapter.enableForegroundDispatch
with empty intent filters and tech lists. This causes the plugin to capture all NFC events even if no listeners are added.
From NfcAdapter.html#enableForegroundDispatch
If you pass null for both the filters and techLists parameters that acts a wild card and will cause the foreground activity to receive all tags via the ACTION_TAG_DISCOVERED intent.
If you create a Cordova app that uses phonegap-nfc, but don't add any listeners, you still see plugin logging scans and firing events without any listeners being added. It shouldn't do this.
Here's what I think happens when the first scan is missed
nfc.addNdefListener()
is called. The intent filters and tech lists are updated, but the NFC adapter is not aware of the changes, it's using the old dataonPause
and onResume
for the pluginonResume
re-starts NFC foreground dispatching with the new intent filters and tech listsSo why does this work OK with some app or some phones? I think it a timing issue when the plugin is initialized and when the listeners are added. @homer-jay's Steps to reproduce wasn't showing the my phones. Maybe it's because I always add my listeners in onDeviceReady. If I delay the listeners, I can duplicate this every time.
// add this to app.onDeviceReady
setTimeout(function() {
nfc.addNdefListener(
function(){
console.log("Tag detected");
},
function(){
console.log("listener registered");
},
function(e){
console.error("Error registering listener: " + e);
}
);
}, 3000);
What's the solution?
Description
In Android,
addNdefListener
is not calling the callback on the first attempt. Any successive call works ok.I've debugged the plugin java source code, and placed breakpoints in the
parseMessage
method. In the first tag read, the action isACTION_NDEF_DISCOVERED
, while every succesive read over the same tag produces anACTION_TECH_DISCOVERED
action. The plugin class fires a "ndef-mime" event in the first case, and regular "ndef" events for every successive tag read. As a consecuence of this, the callback foraddNdefListener
is not fired for the first attempt, since these are only relayed to callbacks registered withaddMimeTypeListener
instead.This was tested using Cordova v3.7.2.
Adding
<param name="onload" value="true" />
to the res/config.xml file did not fix the issue.Steps to reproduce
Select a tag that your phone supports. Write a single plain text record (not MIME, just a WELL_KNOWN of type T) You can use any external app to verify and write the tags (e.g.: NXP's TagInfo and TagWriter are free to download from Google Play).
then in your Cordova app, run the following javascript:
Do not register any other listener.
Expected result
The "Tag detected" line should be logged each time a properly formatted NDEF tag is read.
Actual result:
In the first attempt the plugin doesn't notify the callback even though it has detected the tag and logged the event. Consecutive reads over the very same tag work fine, and the "Tag detected" line is logged.
Examples:
TEST 1 Device: Samsung Galaxy S3 Mini OS: 4.1.2 Tag type: Mifare Classic
First attempt log:
var e = document.createEvent('Events'); e.initEvent('ndef-mime'); e.tag = {"isWritable":true,"id":[12,31,18,17],"techTypes":["android.nfc.tech.NfcA","android.nfc.tech.MifareClassic","android.nfc.tech.Ndef"],"type":"NFC Forum Type 2","canMakeReadOnly":true,"maxSize":716,"ndefMessage":[{"id":[],"type":[84],"payload":[2,101,115,104,101,108,108,111],"tnf":1}]}; document.dispatchEvent(e);
Succesive attempts log:
var e = document.createEvent('Events'); e.initEvent('ndef'); e.tag = {"isWritable":true,"id":[12,31,18,17],"techTypes":["android.nfc.tech.NfcA","android.nfc.tech.MifareClassic","android.nfc.tech.Ndef"],"type":"NFC Forum Type 2","canMakeReadOnly":true,"maxSize":716,"ndefMessage":[{"id":[],"type":[84],"payload":[2,101,115,104,101,108,108,111],"tnf":1}]}; document.dispatchEvent(e);
TEST 2 Device: Samsung Galaxy Core Prime LTE OS: 4.4.4 Tag type: ICODE SLIX
First attempt log:
var e = document.createEvent('Events'); e.initEvent('ndef-mime'); e.tag = {"isWritable":true,"id":[-31,-96,70,0,80,1,4,-32],"techTypes":["android.nfc.tech.NfcV","android.nfc.tech.Ndef"],"type":"android.ndef.unknown","canMakeReadOnly":false,"maxSize":106,"ndefMessage":[{"id":[],"type":[84],"payload":[2,101,115,104,101,108,108,111],"tnf":1}]}; document.dispatchEvent(e);
Succesive attempts log:
var e = document.createEvent('Events'); e.initEvent('ndef'); e.tag = {"isWritable":true,"id":[-31,-96,70,0,80,1,4,-32],"techTypes":["android.nfc.tech.NfcV","android.nfc.tech.Ndef"],"type":"android.ndef.unknown","canMakeReadOnly":false,"maxSize":106,"ndefMessage":[{"id":[],"type":[84],"payload":[2,101,115,104,101,108,108,111],"tnf":1}]}; document.dispatchEvent(e);
TEST 3 Device: Samsung Galaxy A3 OS: 5.0.2 Tag type: ICODE SLIX
Result: same as previous tests.
TEST 4 Device: Huawei Ascend G300 OS: 4.0.3 Tag type: ICODE SLIX
Result: same as previous tests.