Aritz
Aritz

Reputation: 31649

Not proper action shown for NFC intents

I'm dealing with an Android NFC equiped device, just trying to read a tag and receive the information from an Activity. The tag is of the type MifareUltralight. Just reading about that case in the Android docs it seems that I firstly have to register the Intent to launch my Activity in the manifest file and later on I'll be able to receive the NFC Intent from it. That's how my manifest file looks like:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mycompany.android">

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

    <application
        android:allowBackup="true"
        android:label="Test">

        <activity
            android:name="com.mycompany.android.activities.NfcTestActivity"
            android:launchMode="singleTop"
            android:label="Test">
            <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="android.nfc.action.TECH_DISCOVERED"/>
            </intent-filter>
            <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
                android:resource="@xml/nfc_tech_filter" />
            <intent-filter>
                <action android:name="android.nfc.action.TAG_DISCOVERED"/>
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

I set the nfc feature as not required because is not essential for my app to work. Regarding to the NFC tech filters, that's my nfc_tech_filter.xml file:

<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>

Well, having configured that, it seems that my NfcTestActivity should be able to receive NFC Intents. That's how I implement it:

public class NfcTestActivity  extends Activity {

    final String TAG = NfcTestActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i(TAG, "IntentAction (onCreate): " + getIntent().getAction());
        NfcManager manager = (NfcManager) getSystemService(Context.NFC_SERVICE);
        NfcAdapter adapter = manager.getDefaultAdapter();
        if (adapter != null && adapter.isEnabled()) {
            Log.i(TAG, "NFC adapter is enabled");
        } else {
            Log.i(TAG, "NFC adapter is disabled");
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.i(TAG, "IntentAction (onNewIntent): " + getIntent().getAction());
    }

    @Override
    public void onResume() {
        super.onResume();
        Log.i(TAG, "IntentAction (onResume): " + getIntent().getAction());
    }

}

When I start the application I get the following logs:

05-28 10:41:52.044  31182-31182/com.mycompany.android I/NfcTestActivity? IntentAction (onCreate): android.intent.action.MAIN
05-28 10:41:52.054  31182-31182/com.mycompany.android I/NfcTestActivity? NFC adapter is enabled
05-28 10:41:52.054  31182-31182/com.mycompany.android I/NfcTestActivity? IntentAction (onResume): android.intent.action.MAIN

Later on, I read my tag and this log is shown:

05-28 10:41:59.661  31182-31182/com.mycompany.android I/NfcTestActivity? IntentAction (onResume): android.intent.action.MAIN

This means the Activity#onResume method being called when the tag is read, as the documentation says, however, what I get here is an android.intent.action.MAIN instead of the NFC one (ACTION_NDEF_DISCOVERED, ACTION_TECH_DISCOVERED or ACTION_TAG_DISCOVERED).

Moreover, as a clue, when I remove this code piece from my Activity declaration, no onResume log appears when I read the tag:

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

So it seems the TAG_DISCOVERED action is definitelly launched in some way, not the other ones, but I cannot properly catch it from the Activity.

Does anybody know about that?

Upvotes: 2

Views: 708

Answers (2)

Michael Roland
Michael Roland

Reputation: 40831

First of all, your nfc_tech_filter.xml is not correct. The filter

<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>

means that you want to match a tag that is IsoDep and NfcA and NfcB and NfcF and ... However, the NfcX tag technologies are mutually exclusive to each other (so are Ndef and NdefFormatable), so the filter will not match any tag. What you would actually want is something like that:

<tech-list>
    <tech>android.nfc.tech.NfcA</tech>
</tech-list>
<tech-list>
    <tech>android.nfc.tech.NfcB</tech>
</tech-list>
<tech-list>
    <tech>android.nfc.tech.NfcF</tech>
</tech-list>
<tech-list>
    <tech>android.nfc.tech.NfcV</tech>
</tech-list>

Which would just match any tag technology. Or if you want to only match MifareUltralight, you could simply use:

<tech-list>
    <tech>android.nfc.tech.MifareUltralight</tech>
</tech-list>

Using the TAG_DISCOVERED intent filter is generally not a good idea. It is only meant as a fallback and for backward compatibility with the first Android NFC API.

When you want to receive NFC intents while your app is in the foreground, it is generally better to register for the foreground dispatch system, instead of relying on intent filters in the app's manifest. This will assure that your activity receives the events and that no activity chooser is shown even if another app also has intent filters for these events.

Upvotes: 1

Aritz
Aritz

Reputation: 31649

After some brainstorming, I've realized that defaulting android.nfc.action.TAG_DISCOVERED instead of android.nfc.action.NDEF_DISCOVERED does the work for me:

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

I still don't know why I should go with TAG_DISCOVERED when my tag type is covered by TECH_DISCOVERED, which is preferable to use. Anyway, that's the code I use to get the tag id later on:

@Override
public void onResume() {
    super.onResume();
    Log.d(TAG, "onResume: action-"+getIntent().getAction());
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(getIntent().getAction())) {
        Tag tag = getIntent().getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String tagId = bytesToHexString(tag.getId());
        Log.i(TAG, "NFC tag: " + tagId);
    }
}

See also:

Upvotes: 2

Related Questions