Search code examples
androidkotlinnfc

How to create an android app to read URLs via NFC?


I try to create an app to read nfc messages from other devices:

AndroidManifest.xml

...
    <activity android:name=".NFCActivity">
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
            <action android:name="android.nfc.action.TECH_DISCOVERED" />
            <action android:name="android.nfc.action.TAG_DISCOVERED" />
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
    </activity>

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

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

My activity:

class NFCActivity : AppCompatActivity() {
    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)

        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
                val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }

                println(String(messages[0].records[0].payload));
            }
        }

        val tag: Tag? = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
        println(tag)
    }
}

my techlist:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
    <tech-list>
        <tech>android.nfc.tech.IsoDep</tech>
        <tech>android.nfc.tech.NfcA</tech>
        <tech>android.nfc.tech.NfcB</tech>
        <tech>android.nfc.tech.NfcF</tech>
        <tech>android.nfc.tech.NfcV</tech>
        <tech>android.nfc.tech.Ndef</tech>
        <tech>android.nfc.tech.NdefFormatable</tech>
        <tech>android.nfc.tech.MifareClassic</tech>
        <tech>android.nfc.tech.MifareUltralight</tech>
    </tech-list>
</resources>

When sending URLs (from two different devices) the activity doesn't get called. Has anybody an idea for a solution?

The source you can find here: https://github.com/enthusiasmus/nfc

Thanks in advance!


Solution

  • The NFCActivity will be getting called but because it does nothing you will never know.

    There are two possible entry points for the NFC data when using the old style of getting the NFC data via Intents.

    1) Your Activity is already running and if you have enableForegroundDispatch https://developer.android.com/guide/topics/connectivity/nfc/advanced-nfc#foreground-dispatch then the Intent generated by the system will be passed to onNewIntent but as your NFCActivity is not running and you have not enableForegroundDispatch then it won't be passed to onNewIntent

    2) You have NFC Intent filters set and your App is not running then your activity is started for the first time and then you would process the Intent in onCreate not in onNewIntent

    This is because docs for onNewIntent say

    https://developer.android.com/reference/android/app/Activity#onNewIntent(android.content.Intent)

    when the activity is re-launched while at the top of the activity stack instead of a new instance of the activity being started, onNewIntent() will be called on the existing instance with the Intent that was used to re-launch it.

    As you Activity is not RE -launched onNewIntent is not called.

    Most App's I've seen move the Intent processing to a separate method e.g. readFromIntent Then from onCreate call readFromIntent when the App is started by the NFC filters or From onNewIntent call readFromIntent when the App is already running and enableForegroundDispatch has cause the system to send the NFC Intent to the already running app.

    Also you might find that Intent won't come in as NDEF_DISCOVERED as your don't have a data type set in your NFC Filters

    e.g.

    
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="*/*" />
    </intent-filter>
    
    

    From https://developer.android.com/guide/topics/connectivity/nfc/nfc#ndef-disc but */* will get all type, you can reduce the scope down later if you need.

    A full example at https://www.codexpedia.com/android/android-nfc-read-and-write-example/