Wednesday, December 12, 2012

ApplicationCraft - Easy Notifications with Xtify & Pusher






Background

This is one of a series of blogs I'm writing as I build Easy Peasy Task List.  If you want to know about the other related blogs, visit Status of discovery / learning exercises.

Overview

This blog is about learning how to notify connected or unconnected native mobile app users and also desktop browser web apps.  ApplicationCraft makes it very easy to build both web apps and mobile native apps.  So we'll be looking out how all this notification can be done with a single code base and deployed as a web app on the mobile and desktop and as a native mobile app.

I'll be using Xtify to do notifications to a native app.  The notifications will be sent to the mobile device and made available through the devices notification manager if the app is not running.  If the app is running, the app receives the notification.

I'll be using Pusher to do real time notifications to connected browsers running either on the desktop or on the mobile.

Video

The following video gives a brief overview of how AC utilizes Xtify and Pusher to notify connected and unconnected users.



Live App

Because this app uses my personal keys from Xtify, the Xtify portion won't work as I've changed the values. I've also changed the Pusher keys/secret values but if you have your own values, you can enter them in the UI and validate the Pusher portion works.

The app is here: http://acft.ws/nnqe

Source Code

All the source code for the ApplicationCraft app and the Pusher javascript class is located here: https://github.com/bartonhammond/TaskList

Xtify

Note: I'm only showing how to set up the Android.

Xtify is provided by ApplicationCraft as a plugin for the AC Mobile Build.  You can read about setting Xtify up by starting here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/

You first start here: http://developer.android.com/guide/google/gcm/gs.html and then take the link with "Google APIs Console Page".  On the following picture you'll create a API Project - be sure to name the Project ID with some meaning text.

On your API Access tab you will want to copy the API Key:


Now over to Xtify,  once you've registered you'll go to console and then App Manager -> App Key Manager which is at https://console.xtify.com/manage-app-keys.   Here you'll add a new Application and provide the Google API Key from the previous image above.


Then you have to go to the App Manager -> API Keys Manager and "Add New API Key" and be sure to add the Advanced Key Type:

Let's look at the code required for this

Now we're almost ready to send a message but first we need to set up a few things.  The app needs some configuration which is documented here: http://www.applicationcraft.com/developers/documentation/ac-mobile-build-phonegap/ac-mobile-build/ac-build-plugins/xtify-push-notifications/configuring-your-app/

Once you have the configuration in place and the code added to the "handler_application_onAppStarted()" function, we need to invoke the SSJS (Server Side JavaScript) to do the Push Notification.  Now typically the SSJS would be written to respond to a DB commit and notify everyone of the change.  Or in my case the SSJS function will be monitoring for Date/Time reminders by checking the database.  When a reminder is due, a notification will be sent out.

On the client side we have a simple function:
/**
 * Test my device using XID
 */
function handler_sendMessageBtn_onClick(mouseev){
    var xid = app.getData("xidTxt");
    debugger;
 app.callSSJ('pushMessage',function(foo) {
       console.log(foo);
 }, [xid]);
}
We're call the SSJS function "pushMessage" and provide the XID from the textField from line 5.

On the SSJS function we have:
function pushMessage(xid) {
    var url = "http://api.xtify.com/2.0/push";
    var apiKey = "fced37b9-0a50-40ba-a0d9-6fd4dc018dcd"; //https://console.xtify.com/api-keys-manager
    var appKey = "ac7d8afd-50d7-4af5-a312-ae3c883adfb2"; //https://console.xtify.com/manage-app-keys 
    var headers = {"Content-Type": "application/json"};
    
    
    var request = buildPushRequest(apiKey, appKey, xid);
    
    var response = ssj.httpRequest(url, "POST", ssj.OBJ2JSON(request),"string", headers);

    return response;
}

function buildPushRequest(apiKey, appKey, xid) {

    var request = {
        "apiKey": apiKey,
        "appKey": appKey,
        "xids": [ xid ],
        "content": {
            "subject": "Test Subject Barton",
            "message": "Test message Barton"
        }
    }
    
    return request;

}

The function "pushMessages" has the apiKey and appKey defined.  The apiKey is from //https://console.xtify.com/api-keys-manager while the appKey is from //https://console.xtify.com/manage-app-keys

The function "buildPushRequest" just builds an object and substitues the apiKey, appKey and the one XID that we're targeting.

On line 10 the SSJS httpRequest method is used to send the Push Notification.  Note that the request object on line 10 has to be converted into JSON with the "ssj.OBJ2JSON" function.

You're now ready to build the native Android app.

AC Mobile Build

After you configure your app, then you can proceed to the AC Mobile Build.  The build is very straight forward - just close your app and from the console select Mobile Build -> AC Mobile Build.  For Android, I used the default certificate.

Install the generated app on to your Android device.  You don't have to run it but if your do, go ahead and close it so this next part works right.

Sending A Push Notification with Xtify

You're now ready to send a Notification to your Android.  You'll have to run the native app once on the Android and click the button "Display Xid on Mobile" and copy that.  You'll want to close the Android app and bring up your desktop browser and past the XID into the second section (see the video above).

Click the "Send Mobile Notification" and watch your Andriod get notified.  You can then click on the notification and it will start up your native app.

Pusher

Let's look at the SSJS Pusher code

Now on to using Pusher to send real time notifications to connected browsers.  First let's look at the code that was used to make all this possible.

I wrote an Pusher javascript class that requires a class to do Crypto from Google call crypto-js.  Within AC on the server side, I created a new page and copied / pasted the code from Google.

You'll want to do the same thing with my Pusher class - just copy/paste into a Server page. Below is the implementation of the ApplicationCraft version of SSJS Pusher:
//This is based of the work of
//https://github.com/fabrik42/pusher
//
//
//For details see: http://pusher.com/docs/rest_api

/**
 * Constructor expects options as:
 * 
 * {
        appId:  YOUR_app_id,
        appKey: YOUR_auth_key,
        secret: YOUR_auth_secret
    }
    */
function Pusher(options) {
    this.options = options;
    this.url = 'http://api.pusherapp.com';
    this.headers = {"Content-Type": "application/json"};
}
/**
 * Return a channel
 */
Pusher.prototype.channel = function(chan){
    this.options.channel = chan; 
    return this;
};
/**
 * Trigger the event for this channel
 */
Pusher.prototype.trigger = function(event, data) {
    this.event = event;
    this.data = data;
    
    this.jsonData = ssj.OBJ2JSON({"name": event,"channels":[this.options.channel],
        "data":JSON.stringify(data)});
    
    this.body_md5 = ssj.md5(this.jsonData);
        
    var response = ssj.httpRequest(this.path(),
        "POST", 
        this.jsonData,
        "string", this.headers);
};
/**
 * Return the url path to POST to
 */
Pusher.prototype.path = function() {
    return this.url 
        + this.uri() + '?' 
        + this.queryString() 
        + '&auth_signature=' + this.signature();
};
/**
 * Return the uri
 */
Pusher.prototype.uri = function() {
    return '/apps/' + this.options.appId + '/events';
};
/**
 * Return the queryString with the body_md5 
 */
Pusher.prototype.queryString = function() {
    var timestamp = parseInt(new Date().getTime() / 1000);
    return [
      'auth_key=',        this.options.appKey,
      '&auth_timestamp=', timestamp,
      '&auth_version=',   '1.0',
      '&body_md5=',       this.body_md5,
    ].join('');
}
/**
 * Return the Google HmacSHA256 for the querystring
 */ 
Pusher.prototype.signature = function() {
    var signData = ['POST', this.uri(), this.queryString()].join('\n');
    var crypto = CryptoJS.HmacSHA256(signData, this.options.secret);
    return crypto.toString();
};

The class is documented and I won't go into a lot of detail here.  I based this class off a node.js version I found here: https://github.com/fabrik42/pusher

The class Pusher is defined on lines 16-20.  It assumes that the options object will be passed in which has your app_id, auth_key and secret.  These are the values that you entered via the UI.

Pusher.com documents how the Restful API for the server is done.  You can read about it here: http://pusher.com/docs/rest_api.  If you follow through the Pusher class along with the reference, you should be able to follow along.  There's some tricky javascript stuff but not too bad.

Note on line 77 the use of the Google CrytoJS.HmacSHA256 function.

Let's see how easy it is to use this Pusher class.
/**
 * Pusher notification message to channel/event
 */
function pushNotification(app_id, auth_key, auth_secret, channel, event, message) {
    //var app_id = '33490';
    //var auth_key = '282e38c72e6f4fd8ff8b';
    //var auth_secret = '3c78d3ce5911feead471';
    
    debugger;
    var pusher = new Pusher({
        appId:  app_id,
        appKey: auth_key,
        secret: auth_secret
    });
    
    var channel = pusher.channel(channel);


    channel.trigger(event, message);

}
This SSJS function has all the required fields passed to it from the client side code.  On line 10, a new instance of Pusher is created and a object with the appId, appKey, and secret is passed as the options.

On line 16, the channel is defined and a instance is returned.

On line 19, the event for this channel is triggered - it's fired or sent in other words.  It fires with the message.

The SSJS Pusher class makes all this very easy.

Now let's look at the Pusher Client

Before you use Pusher on the client you have to include the file "http://js.pusher.com/1.12/pusher.min.js".  You do this in the Application Properties -> Preload Files.

The following code demonstrates how to subscribe the client to the channel and event:
/**
 * Subscribe to Pusher event
 */
function handler_subscribeBtn_onClick(mouseev){
    //key is from http://app.pusherapp.com/apps/xxxx/api_access
    var pusher = new Pusher(app.getData('appKeyTxt'));
    var channel = pusher.subscribe(app.getData('channelTxt'));
    channel.bind(app.getData('eventTxt'), function(data) {
       alert('just got a mesesage: ' + JSON.stringify(data));
    }); 
}

On line 6 a new Pusher is constructed with the appKey value from the textbox.

On line 7, the pusher object subscribes to the channel which is provided by the textbox.

On lines 8-10, the channel is bound to the event and the callback function is used everytime a message is received.  In this case, on line 9, an alert displays the stringified data.

Summary

Using both Xtify and Pusher with ApplicationCraft is rather easy.  Admittedly it was somewhat of a challenge to figure it out and write the Pusher object only because there wasn't a server side library available for AC.  But for those that are wanting to use Pusher, it should be rather straight forward now.  I think the Xtify is more difficult to setup but if you follow the docs it will make sense.  Hopefully this blog helps with some of that setup.

I hope you enjoyed this blog.