Filtering for NFC Intents:
To start your application when an NFC tag that you want to handle is scanned, your application can filter for one, two, or all three of the NFC intents in the Android manifest.
Usually you want to filter for the
ACTION_NDEF_DISCOVERED intent for the most control of when your application starts.But ACTION_TECH_DISCOVERED intent is a fallback for ACTION_NDEF_DISCOVERED when no applications filter for ACTION_NDEF_DISCOVERED or for when the payload is not NDEF. Filtering for ACTION_TAG_DISCOVERED is usually too general of a category to filter on. Many applications will filter for ACTION_NDEF_DISCOVERED or ACTION_TECH_DISCOVERED before ACTION_TAG_DISCOVERED, so your application has a low probability of starting. ACTION_TAG_DISCOVERED is only available as a last resort for applications to filter for in the cases where no other applications are installed to handle the ACTION_NDEF_DISCOVERED or ACTION_TECH_DISCOVERED intent.
Because NFC tag deployments vary and are many times not under your control, this is not always possible, which is why you can fallback to the other two intents when necessary. When you have control over the types of tags and data written, it is recommended that you use NDEF to format your tags. The following sections describe how to filter for each type of intent.
ACTION_NDEF_DISCOVERED:
To filter for
ACTION_NDEF_DISCOVERED intents, declare the intent filter along with the type of data that you want to filter for. The following example filters for ACTION_NDEF_DISCOVERED intents with a MIME type of text/plain:<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain" />
</intent-filter>
The following example filters for a URI in the form of
http://shaikhhamadali.blogspot.com/p/home.html.<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="http"
android:host="shaikhhamadali.blogspot.com"
android:pathPrefix="/p/home.html" />
</intent-filter>
ACTION_TECH_DISCOVERED:
If your activity filters for the
ACTION_TECH_DISCOVERED intent, you must create an XML resource file that specifies the technologies that your activity supports within a tech-list set. Your activity is considered a match if a tech-list set is a subset of the technologies that are supported by the tag, which you can obtain by calling getTechList().
For example, if the tag that is scanned supports MifareClassic, NdefFormatable, and NfcA, your
tech-list set must specify all three, two, or one of the technologies (and nothing else) in order for your activity to be matched.
so We must specify the technology we are interested in. For this purpose, we create a subfolder called xml in the res folder. In this folder we create the file nfc_filter.xml, in which we specify the technologies.
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<tech-list>
<tech>android.nfc.tech.Ndef</tech>
<!-- class name -->
</tech-list>
</resources>
<!-- other tags for NFC
<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>
-->
Basic Application on This NDEF:
OnLaunch
On Tag Read
Create new Android Project
Project Name: NFC
//tested from 2.3.3 to current android sdk
Build Target: Android 2.3.3 //or greater than that
Application Name: NFC
Package Name: com.shaikhhamadali.blogspot.nfc
Create layout file: activity_nfcdetails
Project Name: NFC
//tested from 2.3.3 to current android sdk
Build Target: Android 2.3.3 //or greater than that
Application Name: NFC
Package Name: com.shaikhhamadali.blogspot.nfc
Create layout file: activity_nfcdetails
1.create layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".NFCDetails" >
<TextView
android:id="@+id/tV_ReadNFC"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="@string/attachNFCToRead"
android:textSize="30sp" />
</RelativeLayout>
2.code of activity:
package com.shaikhhamadali.blogspot.nfc;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter.MalformedMimeTypeException;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.Ndef;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;
/**
* Activity for reading data from an NDEF Tag.
*
* @author Hamad Shaikh
*
*/
public class NFCDetails extends Activity {
public static final String MIMETYPE_TEXT_PLAIN = "text/plain";
public static final String TAG = "NfcTut";
private TextView tV_ReadNFC;
private NfcAdapter nfcAdapt;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nfcdetails);
//initialize control
tV_ReadNFC = (TextView) findViewById(R.id.tV_ReadNFC);
//initialise nfc adapter
nfcAdapt = NfcAdapter.getDefaultAdapter(this);
//check is NFC adapter initialized null when device doesn't support NFC
if (nfcAdapt == null) {
// device deosn't support nfc
Toast.makeText(this, "your device doesn't support NFC.", Toast.LENGTH_SHORT).show();
finish();
return;
}
//check is NFC adapter feature enabled
if (!nfcAdapt.isEnabled()) {
tV_ReadNFC.setText("NFC is disabled.");
} else {
tV_ReadNFC.setText(R.string.attachNFCToRead);
}
handleIntent(getIntent());
}
@Override
protected void onResume() {
super.onResume();
/*
* It's important, that the activity is in the foreground (resumed). Otherwise
* an IllegalStateException is thrown.
*/
requestForegroundDispatch(this, nfcAdapt);
}
@Override
protected void onPause() {
//Call this before onPause, to avoid an IllegalArgumentException.
stopForegroundDispatch(this, nfcAdapt);
super.onPause();
}
@Override
protected void onNewIntent(Intent intent) {
/*
* This method gets called, when a new Intent gets associated with the current activity instance.
* Instead of creating a new activity, onNewIntent will be called.
* In our case this method gets called, when the user attaches a Tag to the device.
*/
handleIntent(intent);
}
private void handleIntent(Intent intent) {
//get action from intent
String action = intent.getAction();
//is action matches the NDEF_DISCOVERED
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
//what is the mime type
String type = intent.getType();
//is text plain or not
if (MIMETYPE_TEXT_PLAIN.equals(type)) {
//create tag instance and retrieve extended data from intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//execute background task
new NdefReaderBgTask().execute(tag);
} else {
Log.d(TAG, "mime type is not text/plain: " + type);
}
}
//is action matches the ACTION_TECH_DISCOVERED
else if (NfcAdapter.ACTION_TECH_DISCOVERED.equals(action)) {
// In case we would still use the Tech Discovered Intent
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
//get the available technologies
String[] techList = tag.getTechList();
//get class name
String searchedTech = Ndef.class.getName();
for (String tech : techList) {
//tag matched then execute background task
if (searchedTech.equals(tech)) {
new NdefReaderBgTask().execute(tag);
break;
}
}
}
}
/**
* @param act The corresponding {@link Activity} requesting the foreground dispatch.
* @param adp The {@link NfcAdapter} used for the foreground dispatch.
*/
public static void requestForegroundDispatch(final Activity act, NfcAdapter adp) {
//create instance of intent
final Intent intent = new Intent(act.getApplicationContext(), act.getClass());
//set flags on top
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
//crate instance of pending intent
final PendingIntent pendingIntent = PendingIntent.getActivity(act.getApplicationContext(), 0, intent, 0);
//create intent filters array
IntentFilter[] filters = new IntentFilter[1];
//create 2D array of techlist String
String[][] techList = new String[][]{};
// Note: This is the same filter as in our manifest.
filters[0] = new IntentFilter();
filters[0].addAction(NfcAdapter.ACTION_NDEF_DISCOVERED);
filters[0].addCategory(Intent.CATEGORY_DEFAULT);
try {
//add data type
filters[0].addDataType(MIMETYPE_TEXT_PLAIN);
} catch (MalformedMimeTypeException e) {
//throw exception on different mime type
throw new RuntimeException("Check your mime type.");
}
//enable foreground dispatch to current activity
adp.enableForegroundDispatch(act, pendingIntent, filters, techList);
}
/**
* @param act The corresponding {@link BaseActivity} requesting to stop the foreground dispatch.
* @param adp The {@link NfcAdapter} used for the foreground dispatch.
*/
public static void stopForegroundDispatch(final Activity act, NfcAdapter adp) {
adp.disableForegroundDispatch(act);
}
/**
* Background task for reading the data. Do not block the UI thread while reading.
* @author Hamad Shaikh
*
*/
private class NdefReaderBgTask extends AsyncTask<Tag, Void, String> {
@Override
protected String doInBackground(Tag... params) {
Tag tag = params[0];
Ndef ndef = Ndef.get(tag);
if (ndef == null) {
// when NDEF is not supported by this Tag.
return null;
}
//Get the NdefMessage that was read from the tag at discovery time.
NdefMessage ndefMessage = ndef.getCachedNdefMessage();
//Get the NDEF Records inside this NDEF Message.
NdefRecord[] records = ndefMessage.getRecords();
for (NdefRecord ndefRecord : records) {
if (ndefRecord.getTnf() == NdefRecord.TNF_WELL_KNOWN && Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
try {
return readNDEFRecordText(ndefRecord);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Unsupported Encoding", e);
}
}
}
return null;
}
private String readNDEFRecordText(NdefRecord record) throws UnsupportedEncodingException {
/*
* See NFC forum specification for "Text Record Type Definition" at 3.2.1
*
* http://www.nfc-forum.org/specs/
*
* bit_7 defines encoding
* bit_6 reserved for future use, must be 0
* bit_5..0 length of IANA language code
*/
// get record pay load variable length
byte[] payload = record.getPayload();
// Get the Text Encoding
String textEncoding = ((payload[0] & 128) == 0) ? "UTF-8" : "UTF-16";
// Get the Language Code
int languageCodeLength = payload[0] & 0063;
// String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
// e.g. "en"
// Get the Text
return new String(payload, languageCodeLength + 1, payload.length - languageCodeLength - 1, textEncoding);
}
@Override
protected void onPostExecute(String result) {
if (result != null) {
tV_ReadNFC.setText("Content in your TAG: " + result);
}
}
}
}
3. Manifest File:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.shaikhhamadali.blogspot.nfc"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="10"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.NFC" />
<uses-feature
android:name="android.hardware.nfc"
android:required="true" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.shaikhhamadali.blogspot.nfc.NFCDetails"
android:label="@string/app_name" >
<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.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
</application>
</manifest>
4. note that:
- you can use this in any way you want.
- you can read other mime types of nfc too.
- about NFC.
5. conclusion:
- Some information about how to Read NFC Tags
- Know how to basic of NFC in my previous post.
- know how to use AsyncTask to avoid UI blocking.
- know the usage of intent filters,permissions and uses-feature and tech-list.
- know what are NFC Intents.
- Some information about how to Read NFC Tags
- Know how to basic of NFC in my previous post.
- know how to use AsyncTask to avoid UI blocking.
- know the usage of intent filters,permissions and uses-feature and tech-list.
- know what are NFC Intents.
6. About the post:
- The code seems to explain itself due to comments, but if you have any questions you can freely ask too!
- Don’t mind to write a comment whatever you like to ask, to know,to suggest or recommend.
- Hope you enjoy it!
7. Source Code:
you can download the source code here
Cheers,
Hamad Ali Shaikh
- The code seems to explain itself due to comments, but if you have any questions you can freely ask too!
- Don’t mind to write a comment whatever you like to ask, to know,to suggest or recommend.
- Hope you enjoy it!
you can download the source code here