Reputation: 41
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
Upvotes: 4
Views: 26586
Reputation: 64700
Foreground dispatch explicitly requires the use of an Activity that's properly configured: it doesn't look like you can use IntentFilter
s 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+"}");
}
Upvotes: 5
Reputation: 604
Your manifest file needs to handle tech discovered intents, in a separately tech filter xml, like this:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</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>
Then your res/xml/nfc_tech_filter.xml must handle the nfc techs you want like this:
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.MifareUltralight</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
<tech-list>
<tech>android.nfc.tech.MifareClassic</tech>
<tech>android.nfc.tech.Ndef</tech>
<tech>android.nfc.tech.NfcA</tech>
</tech-list>
</resources>
Each tech node acts like an AND whereas the tech-list node acts like and OR. I recommend you to first scan your tags with a tool like NFC Tag Reader in order to know your techs.
Then in java code you can enable/disable your foreground dispatch system like other sites already techs, I set up the same techs as I did in xml like this:
private void setUpForegroundDispatchSystem()
{
this.nfcAdapter = NfcAdapter.getDefaultAdapter(this);
this.pendingIntent = 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. */
ndef.addDataScheme("http");
}
catch (MalformedMimeTypeException e) {
throw new RuntimeException("fail", e);
}
this.intentFiltersArray = new IntentFilter[] {ndef};
this.techListsArray = new String[][] { new String[] { MifareUltralight.class.getName(), Ndef.class.getName(), NfcA.class.getName()},
new String[] { MifareClassic.class.getName(), Ndef.class.getName(), NfcA.class.getName()}};
}
You must enable and disable this in Pause and Resume methods. Hope that this info helps you.
Upvotes: 4
Reputation: 2636
If you start the Activity and register the foregroudn dispatch, all you have to do is to register a intent filter for ACTION_TAG_DISCOVERED - this is the lowest filter and matches all discovered tags. If you want to be more specific you would register an intent filter for a tag technology or for a tag that contains ndef.
But if you want to start your app from the homescreen using a tap to a tag, you have do to do this differently. I successfully put a mime message onto a tag and registered that mime type to my activity. Another way is to put a URL to a tag and then register an intent filter that mathces the scheme and host. For mime, this is what you need:
To make this match, the tag needs to have a NDEF mime message with mime as described.
Upvotes: 0
Reputation: 4554
Category element missing from the intent-filter element? See
Android NFC: can we use intent filter with mime type? (android 2.3.3)
You could also try to not use the IntentFilter at all in your code.
Upvotes: 0