Some users of my app are complaining that NFC does not work all the time, on the same NFC tag. Sometimes resetting the NFC option from settings solve the issue.
We have HomeActivity
which receives the NFC intent and delivers it to the required fragment. Here is what I have tried.
Fragment side:
override fun onResume() {
super.onResume()
enableNFCForegroundDispatch()
....
}
override fun onPause() {
mNfcAdapter?.disableForegroundDispatch(requireActivity())
super.onPause()
...
}
private fun enableNFCForegroundDispatch() {
if (mNfcAdapter == null) {
mNfcAdapter =
(requireActivity().getSystemService(AppCompatActivity.NFC_SERVICE) as NfcManager).defaultAdapter
}
val intent = HomeActivity.newIntent(requireContext())
val flag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
PendingIntent.FLAG_MUTABLE
} else 0
val pi = PendingIntent.getActivity(requireContext(), PENDING_INTENT_TECH_DISCOVERED, intent, flag)
if (pi != null) {
mNfcAdapter?.enableForegroundDispatch(
requireActivity(),
pi,
arrayOf(
IntentFilter(
NfcAdapter.ACTION_TECH_DISCOVERED
)
),
arrayOf(arrayOf("android.nfc.tech.NfcV"))
)
}
}
override fun onNewIntent(intent: Intent?) {
// just an implementation by all fragments to handle new intent from the parent
// we don't get this in the error cases
Timber.tag(SENSOR).d("NFC onNewIntent: intent received ${intent?.action} ${intent?.data}")
when (intent?.action) {
NfcAdapter.ACTION_TECH_DISCOVERED -> {
Timber.tag(SENSOR).d("NFC onNewIntent: nfc intent received")
resolveIntent(intent)
}
}
}
Activity side:
companion object {
fun newIntent(context: Context): Intent {
val intent = Intent(context, HomeActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
return intent
}
}
Manifest:
<activity
android:name=".home.HomeActivity"
android:exported="true"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/UiSdkTheme.NoActionBar.NoNavigationBar">
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
nfc_tech_filter:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.NfcV</tech>
</tech-list>
</resources>
I found using the old Intent based method of reading Tags very unreliable, especially if not doing Ndef or doing writing.
This might be because of timing issues when Tags go in and out of range quickly, because to deliver info about the first time a Tag comes in to range it has to Pause
and Resume
your App which can take time and while it does this your App has disableForegroundDispatch
correctly by design and thus might not receive the info about the second time the Tag came in to range.
Also using enableForegroundDispatch
leads to people doing Tag I/O on the UI thread which is against recommendations in the docs and might lead to the I/O being cancelled.
I'm not saying this will solve your problems but I would not use the old enableForegroundDispatch
API for NFC unless you really really need to support very very old Android versions.
I would always use the newer enableReaderMode API for API 19 and higher.
enableReaderMode
does not pause your App to receive data, data is automatically handled in a separate thread so won't cause I/O problems, you have more control of the sound which leads to less errors due to user behaviour and you can work around a bug in some hardware that polls the card too fast.
An example of enableReaderMode