2
votes

I found some recent posts here about reading NFC Tags with Android. The conclusion that I got is that to performing an NFC read action triggers a separated intent.

What I want to achieve is that only my current activity is reading the NDEF message from a NFC tag in text/plain format.

So first question: Is it neccessary to list the intent-filter in my manifest?

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

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

<data android:mimeType="text/plain" />

I think this is not neccessary since I do not want to launch my app via NFC tag event, right?

Second question: How do I keep my NFC reading logic/functions related to my app/activity?

After

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

I go to my current Activity and initialize the NFC Adapter onCreate:

mNfcAdapter = NfcAdapter.getDefaultAdapter(this)

What would be the next step to read the nfc tag NDEF message? Found something related with intent in dispatch foreground:

 @Override
protected void onNewIntent(Intent intent) { 

    handleIntent(intent);
}

Would be nice if someone has an idea/example (Kotlin) how just to read a NFC tag from the activity without stuff like launching/beaming NFC actions.

In iOS e.g. there is a simple NFC session when needed in a VC.

1

1 Answers

3
votes

Correct, if you only want to receive tags while your Activity is in the foreground, you can register for this at runtime. What you're looking for is the enableForegroundDispatch method on NfcAdapter. You can register a PendingIntent for the specific types of tags you want to filter to, and your Activity will receive an Intent in onNewIntent() whenever a tag is detected.

A quick example in Kotlin of what this would look like if you're looking for only IsoDep compatible NFC tags:

override fun onResume() {
    super.onResume()

    NfcAdapter.getDefaultAdapter(this)?.let { nfcAdapter ->
        // An Intent to start your current Activity. Flag to singleTop
        // to imply that it should only be delivered to the current 
        // instance rather than starting a new instance of the Activity.
        val launchIntent = Intent(this, this.javaClass)
        launchIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)

        // Supply this launch intent as the PendingIntent, set to cancel
        // one if it's already in progress. It never should be.
        val pendingIntent = PendingIntent.getActivity(
            this, 0, launchIntent, PendingIntent.FLAG_CANCEL_CURRENT
        )

        // Define your filters and desired technology types
        val filters = arrayOf(IntentFilter(ACTION_TECH_DISCOVERED))
        val techTypes = arrayOf(arrayOf(IsoDep::class.java.name))

        // And enable your Activity to receive NFC events. Note that there
        // is no need to manually disable dispatch in onPause() as the system
        // very strictly performs this for you. You only need to disable 
        // dispatch if you don't want to receive tags while resumed.
        nfcAdapter.enableForegroundDispatch(
            this, pendingIntent, filters, techTypes
        )
    }
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)

    if (NfcAdapter.ACTION_TECH_DISCOVERED == intent.action) {
        val tag = intent.getParcelableExtra<Tag>(NfcAdapter.EXTRA_TAG)
        IsoDep.get(tag)?.let { isoDepTag ->
            // Handle the tag here
        }
    }
}