Building an NFC enabled Android application with PhoneGap

One of the most powerful things about PhoneGap is it’s “Plugin” architecture. If you can write it in native code, you can make it into a plugin.

Near Field Communications (NFC) is a short range, zero configuration, wireless communication. NFC transactions typically happen between a passive device (tag) and an active device (in our case an Android powered phone). The phone generates an RF field which provides enough power to read and write data from the tag. Peer-to-peer communication can also occur between two powered devices (two phones).

Build a Project

If you don’t already have a PhoneGap Android project there a couple of ways to get started:

Checkout phonegap-nfc

Once you have a project up and running you will need to get your hands on either the phonegap-nfc plugin source or distributable.

Add phonegap-nfc to your project

The phonegap-nfc.jar needs to be placed in your projects lib/ directory and the phonegap-nfc.js needs to be placed in your www/assets directory.

Next you need to update you plugins.xml to include the phonegap-nfc plugin:

<plugin name="NfcPlugin" value="com.chariotsolutions.nfc.plugin.NfcPlugin"/>

 

And update your index.html to include phonegap-nfc.js

<script type="text/javascript" charset="utf-8" src="phonegap-nfc.js"></script>

 

Lastly you need to update your AndroidManifest file with:

<uses-permission android:name="android.permission.NFC" />
<uses-feature android:name="android.hardware.nfc" android:required="true" />
<uses-sdk android:minSdkVersion="10" />

Phones and Tags

Now that we have the house work out the way, we really should scan something.

At this point you’ll need some hardware to test you application. For phones and tags you can visit http://www.google.com/nexus/ and http://tinyurl.com/3onvx4w.>

However you can proceed without any hardware. One option is to use the FakeTagsActivity from the good Google folks, as used in their NFC Demo.

You can also look at using Open NFC to simulate NFC tags with the Android Emulator.

A note on tags

If you can get them pre-formatted it will make your transition into the world of reading NFC tags a lot easier. Either way you are going to want to make an app to write tags too :)

Scan some tags!

If you have some pre-formatted tags you can start by adding in a simple NDEF Listener into your application, if your tags aren’t formatted you can jump to the ‘writing’ section.

Wait, what is NDEF?

NDEF stands for, NFC Data Exchange Format (NDEF). It is a specification put together by the NFC Forum for communicating via a defined common format. It provides specifications for transmitting things like mime type objects, plain text, ‘Smart Posters’ and URLS.

The first listener

All interactions in the phonegap-nfc plugin are event based. You can register for one (or more) ‘listeners’ and the plugin will return the corresponding event when appropriate. See the README for a full list of the available listeners.

The definition of the NDEF listener looks like this:

nfc.addNdefListener(callback, [onSuccess], [onFailure]);

For our first implementation we will just log the events. Place the following block in your existing 'ondeviceready' callback.

nfc.addNdefListener(
    function() {
        document.write("Found an NDEF formatted tag");
    },
    function() {
        console.log("Success.");
    },
    function() {
        console.log("Fail.");
    }
);

 

Note: We are inlining all our functions, you may want to arrange your code differently :)

Not terribly useful, but a good start. In order to make the implementation slightly more useful we are going to swap out our vanilla ‘NDEF Listener’ for the ‘Mime Type Listener’. The reason we opt to use the mime listener is that it allows you to generate a 1:1 relationship between your tag and your application. If you format your tags with a ‘unique’ mime type, then only your app will start when you scan the tag avoiding the annoying ‘select which application you want to complete this task with’ screen. More importantly you won’t appear under any generic scans for Tag/NDEF, which has the potential to be annoying for users. See the NFC video from Google IO to get a better understanding of why this is good :)

The definition for the mime type listener looks like:

nfc.addMimeTypeListener(mimeType, callback, [onSuccess], [onFailure]);

 

Our implementation looks like:

nfc.addMimeTypeListener(
    "my/mimeType", 

    parseTag,     

    function() {
        console.log("Success.");
    }   ,
    function() {
        console.log("Fail.");
    }
);

 

Note that the second argument isn’t inlined. Since we are going to be doing some work here I broke it out into it’s own named function.

function parseTag(nfcEvent) {
    var records = nfcEvent.tagData;

    for (var i = 0; i < records.length; i++) {
        var record = records[i],
        p = document.createElement('p');
        p.innerHTML = nfc.bytesToString(record.payload);
        display.appendChild(p);
    }
}

 

Our parseTag function is not doing anything too special, it makes some bold assumptions though. Specifically that the tag was formatted in such a manner that calling nfc.bytesToString will return text.

This simple implementation will read a tag formatted with a specific mime type, and attempt to display the data stored on the tag.

What happens when our app isn’t running?

One of the great things about NFC is the ability for this unique matching of mime type to application to open your app whenever the device detects a tag with our mime type. To see this in action you need and an ‘intent fitler’ to your android manifest.

<intent-filter>
    <action android:name="android.nfc.action.NDEF_DISCOVERED" />
    <data android:mimeType="my/mimetype" />
    <category android:name="android.intent.category.DEFAULT" />
</intent-filter>

Write an NDEF message

We’ve talked a lot about reading specific mime types from tags, but how do we get them there? We can re-use some of the code we already have. We can set a listener for any ‘NDEF’ formatted tag, then we can write an NDEF message with our mime type to it.

Lets start with the NDEF listener again

nfc.addNdefListener(
    writeTag,

    function() {
        console.log("Success.");
    },
    function() {
        console.log("Fail.");
    }
);

 

Now the writeTag implementation:

function writeTag(nfcEvent) {
    var mimeType = "my/mimetype";
    var payload = "super secret data";
    var message = nfc.mimeMediaRecord(mimeType, nfc.stringToBytes(payload));

    nfc.write(
      [message], 
      function () {
        console.log("success");
      }, 
      function (reason) {
        console.log("fail");
      }
    );   
}

In progress

The phonegap-nfc plugin is very much in it’s infancy, there may be bugs or things that are just flat out wrong. Our repo is always open, we love issues with examples and we love pull requests even more!

We are working on adding BlackBerry’s NFC support in OS7 to the plugin, as well as expanding the Android functionality.

Going further

There are more complete examples we have used alongside presentations here, as always we appreciate feedback, code even more!

PhoneGap 1.0 Android Plugin updates

With the upcoming release of PhoneGap 1.0 I thought I would point out a couple of nice additions coming for PhoneGap (Android) Plugin developers (and eventually PhoneGap as a whole).

The first addition is the propagation of lifecycle events (onPause, onResume and onNewIntent) to plugins. Plugin developers are now responsible for reacting appropriately if the app has multitasking turned on, but also have access to the lifecycle of the application. Developing the NFC plugin (https://github.com/chariotsolutions/phonegap-nfc) we found this an absolute necessity, so it’s great to see it added in.

Ah the power of Open Source software :)

The second is a new method of loading plugins. To register a plugin now you just need to add your plugin name and class to the plugins.xml (located in res/xml/)

In your JavaScript companion file you can just create a ‘new plugin’, removing the messy addConstructor/addService calls we had before.

An updated example from the ToastPlugin (https://github.com/m00sey/Toasty)

Usage:

This move to plugins.xml is going to be part of a broader PhoneGap wide move to using cross-platform ‘config.xmls’ (a sneak peak here: https://github.com/filmaj/phonegap-android/blob/feature/example/config.xml#L2...) bringing PhoneGap so much closer to truly, write once run everywhere; exciting stuff!

Building a PhoneGap Plugin for Android

Problem:

I’d like to display a native Toast notification to the use in my PhoneGap Android project. Toast

Solution 1:

There is one solution on the PhoneGap user group already – hthttp://bit.ly/pOrBiC

Solution 2:

Don’t do anything. I found this curious quote on the PhoneGap wiki with regard to notifications. “One thing we want to stay away from is giving access to native dialog/notification methods that can be reproduced within your app using HTML, CSS and JS.” – Shrug.

Solution 3:

Produce a badass plugin Damn straight, we don’t want people to have to mess with copy and pasting chunks of Java to make a simple toast work. So we are going to build a jar, some js and let them have at it.

The short version

In order to build any new functionality into PhoneGap you need to build a ‘plugin’. A plugin has 2 parts to it, a Java class (possibly many) which does the grunt work, and a JavaScript api which allows us to ‘execute’ our native Java from the webview.

The files

  • ToastPlugin.java – our plugin, some good ol' Java.
  • phonegap-toast.js – JavaScript monkeying to register our plugin for use.

The Plugin

In order to build a plugin we need a new class which extending PhoneGap’s ‘Plugin’ class. So go ahead and create a new Java class under the src/ directory and extend Plugin. You should end up with something like the following.

The execute function

Depending on how you created the plugin the execute function might seem a bit obfuscated.

  • arg0 – the name of the javascript function that was called.
  • arg1 – the data passed to the javascript function
  • arg2 is a callback ID that is used to lookup the javascript function in a hash.

Building out functionality

I decided for the Toast Plugin I would just have two function available in JavaScript showShort and showLong. That way the only data the developer has to pass in is the message itself. We have an empty plugin, and an idea of an API.

The JavaScript

The basic shell for the JavaScript side of the plugin I used is:

There is not much voodoo here. The only to note is make sure you make the package name and class name exactly in you call to navigator.app.addService To flesh out the plugin I added two functions showShort and showLong (below):

A quick explanation of what is being passed to PhoneGap.exec:

  • {Function} win – The success callback
  • {Function} fail – The fail callback
  • {String} “ToastPlugin” (service) – The name of the service to use
  • {String} “show_long” (action) – Action to be run in PhoneGap
  • {String[]} [message] – Zero or more arguments to pass to the method

The Java

If you take a look at the source in GitHub there is one interesting note. ctx.runOnUiThread(new RunnableToast(toastMessage, Toast.Short)); PhoneGap runs plugins on a separate thread, of course, why would you run the risk of chewing up the UI thread on crappy plugin code! The Toast.makeText().show(); needs to run on the UI thread, so we wrap our call in a Runnable class and pass that to our PhoneGap Activity (ctx) which makes a call to the UI thread to show the Toast.

This is a pattern I’ve found useful for other plugins too.

Packaging

We have some Java files and some JavaScript. Instead of asking the user to copy our Java file and create the correct package structure in their application, we are going to compile the java, the take the compiled files and then package them into a jar file, so the use can download just two files.

  • phonegap-toast.jar
  • phonegap-toast.js

Compile our Java

Package the Jar

No packaging required for the JavaScript file, it is already awesome.

Usage: Let’s test out our new plugin:

  • Copy the phonegap-toast.jar file to the libs directory of your PhoneGap project.
  • If you are building your application from Eclipse add phonegap-toast.jar to your referenced libraries, otherwise add it to your classpath for your build.
  • Now copy the phonegap-toast.js to your assets directory

Add the phonegap-toast.js file to your source:

You can now use the plugin!

BlackBerry 7 Development on Mac

As much as BlackBerry development pains me, nothing pains me more than BlackBerry development on Windows. Currently BlackBerry have a plug-in beta for Mac, but it is for 6. Now BlackBerry 7 is coming out I wanted to check it out.

So here goes:

  • Help –> Install New Software –> Add: http://www.blackberry.com/go/eclipseUpdate/3.6/java
  • From the drop down select ‘BlackBerry Java SDK’ Version: 7.0.0.6
  • Let Eclipse do it’s thing.
  • After a few restarts open up your Eclipse/plugins/net.rim.ejde.componentpack7.0.0_7.0.0.6 folder
  • Edit components/BlackBerry.ee and replace the following configuration:

Open another Finder. Copy over the following files from Eclipse/plugins/net.rim.ejde.componentpack6.0.0_6.0.0.30 to the same location in Eclipse/plugins/net.rim.ejde.componentpack7.0.0_7.0.0.6

  • components/bin/libRIMUsbJni.jnilin
  • components/bin/javaloader
  • components/bin/preverify
  • components/simulator/fledge

Now time to configure Eclipse with BlackBerry 7

  • Open up Eclipse and start a new BlackBerry Project.
  • Click ‘Configure JREs…’
  • hit Add
  • Select JRE Type ‘BlackBerry Execution Environment VM.
  • hit Next
  • Point the definition at Eclipse/plugins/net.rim.ejde.componentpack7.0.0_7.0.0.6/components/BlackBerry.ee

The other fields should be filled in automagically.

Success!

Sadly, the simulator for 7 is only available for Windows, and of course theres no way to get BB7 on a real device. Anyone asked why BlackBerry why they hate their developers so much?

NFC Tip #1

For anyone else poking around with the NFC:

The documentation at http://developer.android.com/guide/topics/nfc/index.html in regard to specifiying the meta data for the android.nfc.action.TECH_DISCOVERED action is wrong. The meta data entry needs to be specified outside the intent filter i.e.

<activity android:name="activityName" android:label="@string/app_name">

<intent-filter>

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

<category android:name="android.intent.category.DEFAULT" />

</intent-filter>

<meta-data android:name="android.nfc.action.TECH_DISCOVERED"

android:resource="@xml/supported_tag_formats" />

</activity>


The issue can be tracked here. If only I thought to check the bugs before wasting another 10 minutes eh? Yes, I know there is a correct example further down the documentation :)

CABasicAnimation. The basics.

I've been using a lot of animations recently to try and give a more polished feel to some of the applications I've been working on. I think subtle use of animation and audio provide useful cues and feedback to the user, above and beyond what is native to iOS.
Here are some snippets of using animations in iOS. You will need to add the QuartzCore.framework to your project.

The short version:
I've thrown together an example app with too many simple animations in it and attached them all to gestures.

https://github.com/m00sey/Gesturemation

Move along x axis

CABasicAnimation *move = [CABasicAnimation animationWithKeyPath:@"transform.translation.x" ]; 
[move setFromValue:[NSNumber numberWithFloat:0.0f]]; 
[move setToValue:[NSNumber numberWithFloat:100.0f]]; 
[move setRemovedOnCompletion:NO]; 
[move setFillMode:kCAFillModeForwards]; 
[move setDuration:1.0f];

Note: Most attributes on CABasicAnimation are trivial. However the most common trip up is the animation resetting once it has completed. Setting the fillMode to kCAFillModeForwards and removedOnCompletion to NO will ensure the animation is applied after the animation has completed. 

Move along y axis
It is the exact same code as above, but with a different KeyPath

@"transform.translation.y"

Rotate

CABasicAnimation *fullRotation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
[fullRotation setFromValue:[NSNumber numberWithFloat:0]];
[fullRotation setToValue:[NSNumber numberWithFloat:((360*M_PI)/180)]];
[fullRotation setDuration:0.5f];

Scale

CABasicAnimation *scale = [CABasicAnimation animationWithKeyPath:@"transform.scale"];
[scale setToValue:[NSNumber numberWithFloat:10.0f]];
[scale setDuration:0.0];
[scale setRemovedOnCompletion:NO];
[scale setFillMode:kCAFillModeForwards];

Opacity
You can do a fade like so:

CABasicAnimation *fadey = [CABasicAnimation animationWithKeyPath:@"opacity"];
[fadey setToValue:[NSNumber numberWithFloat:0.0f]];
[fadey setDuration:1.0f];

Adding an animation to a layer
Once you have created your animation you can need to add it to element you want to animate:

[[target layer] addAnimation:myAwesomeAnimation forKey:@"someKey"];

same thing if you want to add an animation group:

[[target layer] addAnimation:myAwesomeAnimationGroup forKey:@"someKey"];

Animation Groups
If you want to generate animations comprising of multiple effect running simultaneously i.e. Scale and Rotate. You can use an animation group to achieve this:

CAAnimationGroup *animationGroup = [CAAnimationGroup animation];
[animationGroup setAnimations:[NSArray arrayWithObjects:simpleAnimationOne, simpleAnimationTwo, nil]];
[animationGroup setDuration:1.0f];
[animationGroup setAutoreverses:YES];
[[moveMe layer] addAnimation:animationGroup forKey:@"group action"];

Note: All animations within the group will only execute for the group duration, any animation running longer than the group duration will be cut short.

Animation blocks
If you want to run one animation, and when it completes run another, animation blocks make this cake. Most of my favorite animations usually end up being a combination of animation blocks and CABasicAnimation.

[UIView animateWithDuration:0.5 animations:^{ 
        //simple animation one
} completion:^(BOOL finished){
    [UIView animateWithDuration:0.5 animations:^{ 
                         //simple animation two
    } completion:^(BOOL finished){
    }];
}];

Combine for wondrous marvels!

The examples have trivial behavior. Getting the perfect subtle animations can take some tinkering, but I really do think they can add something to the experience :)

A word of warning: Too much animation, like anything and be highly annoying :)

I'd like to follow this post up with some more in depth animating shenanigans in iOS. Stay tuned!

Things.

I finally ponied up the cash and bought Things after using the iPhone version for a while now, and having the desktop trial version.

I purchased Things through the App Store and used AppZapper to remove the demo version.

When I tried to open Things I was met with the following error:

Please update Things. 

Your Library cannot be opened since it was created with a newer version of Things. Please download the latest version of Things and try again.

This stuck me as kind of odd, the App Store version says 1.4.6, the trial version I used was 1.4.6.

Odd!

Regardless:

rm -rf ~/Library/Application Support/Cultured Code

Problem solved.

Touch Events vs Gesture Recognizers

I tend to get pretty set in my ways. I have used the following code in a number of projects for a making a view follow a finger dragging on the screen:

[sender setCenter:[[[event allTouches] anyObject] locationInView:[self view]]];

(code omitted for brevity)

However, on a recent project I found that the code I had been previously using was causing me issues - it fires the 'touch up inside' event when used on a button. This was unfortunate as there were two different actions, one for dragging the button and one for the button press.

I had looked at gesture recognizers a while back, but never really used them. What was useful about using the recognizers this time was that I could apply a UITapGestureRecognizer to a view, and a UIPanGestureRecognizer to the same layer, without any funny shenanigans deciphering events.

The following code when used in conjunction with a UIPanGestureRecognizer will cause a view to follow a finger.

[[recognizer view] setCenter:[recognizer locationInView:[self view]]];

However, on some occasions I would want to know when a pan started or finished, instead of reverting back to my old faithful event based code, you can check the state of the pan gesture. Something like:

if([recognizer state] == UIGestureRecognizerStateBegan) or UIGestureRecognizerStateEnded

A lot more on gesture recognizers to come.