Skip to content

Latest commit

 

History

History
348 lines (266 loc) · 15.1 KB

File metadata and controls

348 lines (266 loc) · 15.1 KB

Near Field Communications overview

  • 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 (?).

Workshop / tutorial targets

  • 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

Prerequsistis

You must know Android development. If you require help with the basics, please try other introductory tutorials first.

Requirements - bring this

Note that Eclipse is merely required for the NDEF editor, so you can still use your favorite editor for the rest of the tasks.

Task 1 - Create new NDEF message and write to tag using tool

a. Open base project

Open imported project HelloWorldNFC Base.

b. Create new NDEF file

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.

c. Add an Android Application Record

  1. Read online documentation about Android Application Records.
  2. 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.

d. Write the NDEF message to a tag.

You now have an NDEF message consisting of a single record. Start the NFC Developer Android app, then

  1. Scan the custom QR code generated by the NDEF editor.
  2. 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.

e. Try out the newly created tag

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.

Task 2 - Discover tag - Hello NFC tag

a. Launch Hello World application via NFC

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.

b. Change Hello World text by scanning a tag

We want to receieve NFC messages when our application is showing on the screen.

  1. 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" />
    
  2. 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);
     	}
    
  3. Call enable/disable foreground mode from onResume(..) and onPause(..) 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);
     }
    
  4. 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.

c. Detect app launch via NFC tag

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.

Task 3 - Read and analyze tag payload

a. Obtain tag NDEF payload

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.

b. Get the NDEF Message.

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.

d. Determine which NDEF records are present on the tag.

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?

e. Get the tag id

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));
	}
}

and verify that it is working. How many bytes is the tag id? Task 4 - Compose and write tag payload

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

b. Write the message when tag is scanned

  1. Convert Message to ndefMessage. We need to convert our high-level objects to the Android native low-level equivalent. Add in method onNewIntent():

     Message composedMessage = composeMessage("1");
     NdefMessage composedMessageNdefMessage = composedMessage.getNdefMessage();
    
  2. 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;
     }
    
  3. 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?

c. Increment the integer stored in the Text Record on tag scan

In the onNewIntent() method

  1. Detect og read the NDEF message and filter out the Text Record.
  2. Get the Text Record text, parse and increment the number
  3. Compose a new NDEF message and write it to the tag
  4. Update the GUI with the resulting number

Hint: Use the instanceof operator to check the record type.

Task 5 - device to device communication: Android Beam

Use Android Beam to exchange information between two devices.

a. Register push message callback interface

Read about the NfcAdapter.CreateNdefMessageCallback interface and register callback in the activity onCreate() method using

    // Register Android Beam callback
    nfcAdapter.setNdefPushMessageCallback(this, this);

b. Compose push message

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();

c. Push message between two devices

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?

d. Read pushed message (on the receiver side).

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.

e. Post push message notification

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?

You are done!

That concludes this workshop / tutorial. The original version of this material can be found at the greenbird Git repo.