Android NFC foreground dispatch problem

user746776 picture user746776 · May 10, 2011 · Viewed 23.1k times · Source

i'm completely new to android development and have to write a simple app for reading an nfc tag (with nexus s) for university.

my problem is that when the nexus discoveres a tag, my app is not listed in the "select an action"-popup. the aim is to read tags using the foreground-dispatch method as described in http://developer.android.com/guide/topics/nfc/index.html and http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/nfc/ForegroundDispatch.html

i think there's something missing in the manifest, but i don't know what. here's the manifest:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.iforge.android.nfc"
>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<application
    android:icon="@drawable/icon"
    android:label="@string/app_name"
>
<activity android:name=".simulator.FakeTagsActivity"
    android:theme="@android:style/Theme.NoTitleBar">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>

</activity>

    <activity android:name="TagViewer"
        android:theme="@android:style/Theme.NoTitleBar"
    >
        <intent-filter>
            <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
            <data android:mimeType="mime/type" />
        </intent-filter>

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

        </intent-filter>

        <intent-filter>
            <action android:name="android.nfc.action.TAG_DISCOVERED"/>
        </intent-filter>
    </activity>
</application>
<uses-sdk android:minSdkVersion="10" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />

here's the code of the activity that should be called when a tag is discovered (which is build out of the android NFCDemo as well as the ForegroundDispatch-example):

public class TagViewer extends Activity 
{

WebView webView;
private NfcAdapter mAdapter;
private PendingIntent mPendingIntent;
private IntentFilter[] mFilters;
private String[][] mTechLists;

@Override
protected void onCreate(Bundle savedInstanceState) 
{
    super.onCreate(savedInstanceState);
    mAdapter = NfcAdapter.getDefaultAdapter(this);
    mPendingIntent = PendingIntent.getActivity(
            this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

    IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
    try {
        ndef.addDataType("*/*");    /* Handles all MIME based dispatches. 
                                       You should specify only the ones that you need. */
    }
    catch (MalformedMimeTypeException e) {
        throw new RuntimeException("fail", e);
    }

    mFilters = new IntentFilter[] {
            ndef,
    };

    mTechLists = new String[][] { new String[] { NfcF.class.getName() } };


    setContentView(R.layout.tag_viewer);
    webView = (WebView) findViewById(R.id.webView1);
    webView.getSettings().setJavaScriptEnabled(true); 
    webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
    webView.getSettings().setPluginsEnabled(false);
    webView.getSettings().setSupportMultipleWindows(false);
    webView.getSettings().setSupportZoom(false);
    webView.setVerticalScrollBarEnabled(false);
    webView.setHorizontalScrollBarEnabled(false);

    resolveIntent(getIntent());
}

@Override
public void onResume() {
    super.onResume();
    mAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}

@Override
public void onPause() {
    super.onPause();
    mAdapter.disableForegroundDispatch(this);
}

void resolveIntent(Intent intent) 
{
    // Parse the intent
    String action = intent.getAction();
    if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(action)) 
    {
        // When a tag is discovered we send it to the service to be save. We
        // include a PendingIntent for the service to call back onto. This
        // will cause this activity to be restarted with onNewIntent(). At
        // that time we read it from the database and view it.
        Parcelable[] rawMsgs = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
        NdefMessage[] msgs;
        if (rawMsgs != null) 
        {
            msgs = new NdefMessage[rawMsgs.length];
            for (int i = 0; i < rawMsgs.length; i++) 
            {
                msgs[i] = (NdefMessage) rawMsgs[i];
            }
        } 
        else 
        {
            // Unknown tag type
            byte[] empty = new byte[] {};
            NdefRecord record = new NdefRecord(NdefRecord.TNF_UNKNOWN, empty, empty, empty);
            NdefMessage msg = new NdefMessage(new NdefRecord[] {record});
            msgs = new NdefMessage[] {msg};
        }
        // Setup the web-view
        setUpWebView(msgs);
    } 
    else 
    {
        Log.e("ViewTag", "Unknown intent " + intent);
        finish();
        return;
    }
}

void setUpWebView(NdefMessage[] msgs) 
{
    if (msgs == null || msgs.length == 0) return;

    String urlToLoad = MessageParser.parseMessage(msgs[0]);
    if(!urlToLoad.matches("")) webView.loadUrl(urlToLoad);

}

@Override
public void onNewIntent(Intent intent) 
{
    setIntent(intent);
    resolveIntent(intent);
    Log.i("Foreground dispatch", "Discovered tag with intent: " + intent);
}
}

i tried a lot, but nothing works. it would be great if anyone can tell me what i'm missing. i'm running out of time :-(

please

thanks

Answer

Femi picture Femi · Jun 28, 2012

Foreground dispatch explicitly requires the use of an Activity that's properly configured: it doesn't look like you can use IntentFilters set up in the AndroidManifest.xml for foreground dispatch (your app must actually be in the foreground, i.e. running). The code below appears to work correctly (I just tested it) in case you're still interested (ACTION_TAG_DISCOVERED was what I was watching for):

private NfcAdapter mAdapter;
    private PendingIntent pendingIntent;
    private IntentFilter[] mFilters;
    private String[][] mTechLists;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.main);

        mAdapter = NfcAdapter.getDefaultAdapter(this);
        pendingIntent = PendingIntent.getActivity(
          this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);

     // Setup an intent filter for all MIME based dispatches
        IntentFilter ndef = new IntentFilter(NfcAdapter.ACTION_NDEF_DISCOVERED);
        try {
            ndef.addDataType("*/*");
        } catch (MalformedMimeTypeException e) {
            throw new RuntimeException("fail", e);
        }
        IntentFilter td = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED);
        mFilters = new IntentFilter[] {
                ndef, td
        };

        // Setup a tech list for all NfcF tags
        mTechLists = new String[][] { new String[] { 
                NfcV.class.getName(),
                NfcF.class.getName(),
                NfcA.class.getName(),
                NfcB.class.getName()
            } };
    }

@Override
    public void onResume()
    {
        super.onResume();

        mAdapter.enableForegroundDispatch(this, pendingIntent, mFilters, mTechLists);
    }

    @Override
    public void onPause()
    {
        super.onPause();
        mAdapter.disableForegroundDispatch(this);
    }

    @Override
    public void onNewIntent(Intent intent){
        // fetch the tag from the intent
        Tag t = (Tag)intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
        String tlist = getTechList(t);
        android.util.Log.v("NFC", "Discovered tag ["+tlist+"]["+t+"] with intent: " + intent);
        android.util.Log.v("NFC", "{"+t+"}");
}