- Close-range radio communication - 4 cm effective range
- Effective for small bursts of data
- Comes in active and passive forms, typically as in an active mobile device and a passive (sticker) tag
- Expected smart-phone penetration of 50% by end of 2013 (?).
- Learn some NDEF format basics
- Compose NDEF messages
- Write to tag using a tool app
- Programmatically read NDEF messages into an Android device with NFC support
- Programmatically compose and write NDEF messages
- Send NDEF messages directly between two devices
You must know Android development. If you require help with the basics, please try other introductory tutorials first.
- A computer (PC, Mac or Linux)
- An NFC-enabled device (Android 4.0 or higher)
- Install Android application 'NFC Developer' from Android Play
- An USB cable to connect computer and device
- Eclipe with Android SDK installed
- Install NFC plugin from update site http://nfc-eclipse-plugin.googlecode.com/git/nfc-eclipse-plugin-feature/update-site/
- Check out this (https://github.com/skjolber/Fagmote.git) Git repository. Alternatively, download zip.
- Add all the projects to your workspace AND MAKE SURE THEY BUILD SUCCESSFULLY. Use the same Android SDK as your phone - update each project correspondingly by right-clicking the project -> Properties -> Android -> Project Build Target and selecting the correct Android SDK version.
Note that Eclipse is merely required for the NDEF editor, so you can still use your favorite editor for the rest of the tasks.
Open imported project HelloWorldNFC Base.
Create a new file in the root of the project using New -> Other -> Near Field Communications -> NDEF File.
Hint: NDEF is a data format used in tags.
- Read online documentation about Android Application Records.
- Create an Android Application Record with package name no.java.schedule using the NDEF editor.
Hint: Open NDEF file and right-click on the table.
You now have an NDEF message consisting of a single record. Start the NFC Developer Android app, then
- Scan the custom QR code generated by the NDEF editor.
- Write by holding a tag to the back of the device.
Hint: If nothing happens, make sure you are holding the tag in the center of the backside of the phone (NFC range is only 4 cm ;-)) Hint: If get tag write IOException, try scanning again a bit slower.
Close the NFC Developer app and navigate to the home screen. Make sure your Android device is online. What happens when you scan the tag?
Hint: If you do not already have an application with identifier no.java.schedule installed, Android will search for it at Google Play.
Launch (run) the empty HelloWorldNFC Base project, so that it gets installed on your device. Update the NDEF message from the previuos task so that the Android Application package is com.helloworld.nfc, and write it to the tag.
We want to receieve NFC messages when our application is showing on the screen.
-
Add NFC permissions in AndroidMainfest.xml:
<!-- Near field communications permissions --> <uses-permission android:name="android.permission.NFC" /> <uses-feature android:name="android.hardware.nfc" android:required="true" />
-
Initialize NFC foreground mode in the Hello World activity:
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // initialize NFC nfcAdapter = NfcAdapter.getDefaultAdapter(this); nfcPendingIntent = PendingIntent.getActivity(this, 0, new Intent(this, this.getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0); }
-
Call enable/disable foreground mode from
onResume(..)
andonPause(..)
in the Hello World activity:public void enableForegroundMode() { Log.d(TAG, "enableForegroundMode"); // foreground mode gives the current active application priority for reading scanned tags IntentFilter tagDetected = new IntentFilter(NfcAdapter.ACTION_TAG_DISCOVERED); // filter for tags IntentFilter[] writeTagFilters = new IntentFilter[] {tagDetected}; nfcAdapter.enableForegroundDispatch(this, nfcPendingIntent, writeTagFilters, null); } public void disableForegroundMode() { Log.d(TAG, "disableForegroundMode"); nfcAdapter.disableForegroundDispatch(this); }
-
Change title to Hello NFC! when a tag is scanned: Add
@Override public void onNewIntent(Intent intent) { // this method is called when an NFC tag is scanned Log.d(TAG, "onNewIntent"); if (NfcAdapter.ACTION_TAG_DISCOVERED.equals(intent.getAction())) { Log.d(TAG, "A tag was scanned!"); TextView textView = (TextView) findViewById(R.id.title); textView.setText("Hello NFC tag!"); vibrate(); // signal detected tag :-) } }
Verify functionality: First start the application. Then scan the tag and see what happens.
So far we have only detected the tag after the app was started. To detect whenever the app itself is launched by scanning our tag, add
<intent-filter>
<action android:name="android.nfc.action.NDEF_DISCOVERED" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="vnd.android.nfc" />
</intent-filter>
and append the onCreate(..)
method with
if(getIntent().hasExtra(NfcAdapter.EXTRA_TAG)) {
TextView textView = (TextView) findViewById(R.id.title);
textView.setText("Hello lauch from NFC tag!");
vibrate(); // signal detected tag :-)
}
Are you able to make the app show the new message?
Hint: Relaunch the app and close it. Then scan the tag from the home screen.
Check for NDEF messages in method onNewIntent(..)
using
Parcelable[] messages = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
if (messages != null) {
Log.d(TAG, "Found " + messages.length + " NDEF messages");
}
Verify that messages appear in the log.
Cast the first messages element into the Android class NdefMessage
:
NdefMessage ndefMessage = (NdefMessage)messages[0];
and log the number of NdefRecords
within the ndefMessage
variable. How does the NdefRecord
class compare to the NDEF records we saw in the NDEF editor in task 1?
Hint: In the present form, the NDEF Record is not very friendly.
c. Parse NDEF Message using the NDEF Tools for Android library
Parse ndefMessage
from previous task into a list of Record
using
// parse to high-level records
try {
List<Record> records = new Message(ndefMessage);
Log.d(TAG, "Found " + records.size() + " records in message");
} catch (Exception e) {
Log.e(TAG, "Problem parsing message", e);
}
Verify that the expected messages appear in the log.
Iterate of the parsed record(s) and investigate their type and contents.
for(int k = 0; k < records.size(); k++) {
Record record = records.get(k);
Log.d(TAG, " Record #" + k + " is of class " + record.getClass().getName());
// TODO: add breakpoint or log statement and inspect record.
}
Did your tag contain the expected content?
The tag id can be a useful attribute. Log the tag id by adding a call to
protected void printTagId(Intent intent) {
if(intent.hasExtra(NfcAdapter.EXTRA_ID)) {
byte[] byteArrayExtra = intent.getByteArrayExtra(NfcAdapter.EXTRA_ID);
Log.d(TAG, "Tag id is " + toHexString(byteArrayExtra));
}
}
a. Compose an NDEF message using the NDEF Tools for Android library
public Message composeMessage(String text) {
Log.d(TAG, "createMessage");
Message message = new Message(); // ndeftools ndef message
// add an android application record
AndroidApplicationRecord aar = new AndroidApplicationRecord("com.helloworld.nfc");
message.add(aar);
// add a text record
TextRecord record = new TextRecord(text);
message.add(record);
return message;
}
-
Convert
Message
tondefMessage
. We need to convert our high-level objects to the Android native low-level equivalent. Add in methodonNewIntent()
:Message composedMessage = composeMessage("1"); NdefMessage composedMessageNdefMessage = composedMessage.getNdefMessage();
-
Write to tag Add the following code. Call the method from
onNewIntent()
if a tag is detected, after the above lines.public boolean write(NdefMessage rawMessage, Intent intent) { Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG); Ndef ndef = Ndef.get(tag); if(ndef != null) { try { Log.d(TAG, "Write formatted tag"); ndef.connect(); if (!ndef.isWritable()) { Log.d(TAG, "Tag is not writeable"); return false; } if (ndef.getMaxSize() < rawMessage.toByteArray().length) { Log.d(TAG, "Tag size is too small, have " + ndef.getMaxSize() + ", need " + rawMessage.toByteArray().length); return false; } ndef.writeNdefMessage(rawMessage); return true; } catch (Exception e) { Log.d(TAG, "Problem writing to tag", e); } finally { try { ndef.close(); } catch (IOException e) { // ignore } } } else { Log.d(TAG, "Write to an unformatted tag not implemented"); } return false; }
-
Update GUI text on write success or failure.
if(write(composedMessageNdefMessage, intent)) { Log.d(TAG, "Write success!"); TextView textView = (TextView) findViewById(R.id.title); textView.setText("Write success!"); } else { Log.d(TAG, "Write failure!"); TextView textView = (TextView) findViewById(R.id.title); textView.setText("Write failure!"); }
then verify that the code is working. Do you see the new Text Record in the log the second time you scan the tag?
In the onNewIntent()
method
- Detect og read the NDEF message and filter out the Text Record.
- Get the Text Record text, parse and increment the number
- Compose a new NDEF message and write it to the tag
- Update the GUI with the resulting number
Hint: Use the instanceof
operator to check the record type.
Use Android Beam to exchange information between two devices.
Read about the NfcAdapter.CreateNdefMessageCallback interface and register callback in the activity onCreate()
method using
// Register Android Beam callback
nfcAdapter.setNdefPushMessageCallback(this, this);
Implement method createNdefMessage(..)
- for composing the NdefMessage
to be pushed:
Log.d(TAG, "createNdefMessage");
Message message = new Message(); // ndeftools ndef message
// add an android application record
AndroidApplicationRecord aar = new AndroidApplicationRecord("com.helloworld.nfc");
message.add(aar);
// create external type record to be pushed
ExternalTypeRecord record = new GenericExternalTypeRecord("com.my.data", "myDataType", "This is my magic payload".getBytes(Charset.forName("UTF-8")));
message.add(record);
// encode one or more record to NdefMessage
return message.getNdefMessage();
Make sure to first disable the tag write functionality.
Hold two devices together and see what happens. When a is message pushed from one NFC device to another?
Are you able to filter out the right External Type record on the reciever side?
Hint 1: A pushed NDEF message can be read the same was as a tag
Hint 2: The Android Application Record is also an [External Type](http://developer.android.com/reference/android/nfc/NdefRecord.html#createExternal(java.lang.String, java.lang.String, byte[]) record.
Use the NfcAdapter.OnNdefPushCompleteCallback interface to show a notification when a message is pushed using Android Beam.
First register callback in onCreate(..)
// Register callback to listen for message-sent success
nfcAdapter.setOnNdefPushCompleteCallback(this, this);
Then implement the onNdefPushComplete(..)
method:
// Handle push success
@Override
public void onNdefPushComplete(NfcEvent nfcEvent) {
Log.d(TAG, "onNdefPushComplete");
runOnUiThread(new Runnable() {
public void run() {
TextView textView = (TextView) findViewById(R.id.title);
textView.setText("Message beamed!");
}
});
}
Verify that everything is working by holding two phones together. Reflect over the user-friendliness of transferring a lot of data holding two devices together in this way - what would be a more practical approach in such a scenario?
That concludes this workshop / tutorial. The original version of this material can be found at the greenbird Git repo.