Example: Using the PPS interface

In the example, called com.qnx.demo, we build a plugin that provides methods and events for creating, updating, and watching a simple PPS object. The example links to the PPS utilities file, ppsUtils.js, which is included as part of the webplatform.js file in the SDK directory. The namespace for ppsUtils.js is qnx.webplatform.pps. The files in the demo follow the structure described in the "Structure of a plugin" section.

Files in the example

This example includes the following files:
  • plugin.xml in the com.qnx.demo directory. This file declares the namespace of the plugin, and describes the file structure.
  • demo.js and index.js in the com.qnx.demo/src/blacberry10 directory. The demo.js file initializes the extension and defines functions for handling events and returning PPS data. The index.js file sets up the events that are triggered.
  • client.js in the com.qnx.demo/www directory. This file defines the externally visible methods that apps can use.

Let's look at some of these files in more detail.

demo.js

The demo.js file provides the core functionality of the plugin. First, we link to the qnx.webplatform.pps namespace and declare some variables we'll use later:

var _pps = qnx.webplatform.pps,
    _readerPPS,
    _writerPPS,
    _triggerUpdate; 

Now we'll create the demo PPS object. There's additional code that handles errors and so on, but let's focus on the PPS object's creation:

_readerPPS = _pps.createObject("/pps/qnxcar/demo", _pps.PPSMode.DELTA);
_readerPPS.onNewData = function(event) {
            if (_triggerUpdate && event && event.data) {
                _triggerUpdate(event.data);            
...

_writerPPS = _pps.createObject("/pps/qnxcar/demo", _pps.PPSMode.DELTA);

...

The preceding code creates a new PPS object (/pps/qnxcar/demo) with two handles: one for reading (_readerPPS) and one for writing (_writerPPS). These handles give us access to the methods in qnx.webplatform.pps that we can use for manipulating the PPS data. The _triggerUpdate object is used in handling events. Here, we use it in defining the action to take when new data is available in our PPS object.

The demo.js file also defines the functions for handling the event trigger (setTriggerUpdate()) and for getting (get()) and setting (set()) the PPS data. These functions are exported so that they can be called from the index.js file:

/**
 * Sets the trigger function to call when an event is fired
 * @param trigger {Function} The trigger function to call
 *         when an event is fired
 */
setTriggerUpdate: function(trigger) {
    _triggerUpdate = trigger;
},
    
/**
 * Returns the demo object
 * @returns {Object} The demo object
 */
get: function(settings) {
     return _readerPPS.data.demo;
},
    
/**
 * Set a demo object field
 * @param {String} key The data key
 * @param {Mixed} value The data value
 */
set: function(key, value) {
     var data = {};
     data[key] = value;
     _writerPPS.write(data);
}

index.js

In the index.js file, we define the functions that we'll make available to our clients through the client.js file:

/**
* Starts triggering events
* @param {Function} success Function to call if the operation is a success
* @param {Function} fail Function to call if the operation fails
* @param {Object} args The arguments supplied
* @param {Object} env Environment variables
*/
startEvents: function(success, fail, args, env) {
    _eventResult = new PluginResult(args, env)
    try {
        _demo.setTriggerUpdate(function (data) {
            _eventResult.callbackOk(data, true);
        });
        _eventResult.noResult(true);
    } catch (e) {
        _eventResult.error("error in startEvents: " + JSON.stringify(e), false);
    }
},

/**
 * Stops triggering events
 * @param {Function} success Function to call if the operation is a success
 * @param {Function} fail Function to call if the operation fails
 * @param {Object} args The arguments supplied
 * @param {Object} env Environment variables
 */
stopEvents: function(success, fail, args, env) {
   var result = new PluginResult(args, env);
   try {
       //disable the event trigger
        _demo.setTriggerUpdate(null);
        result.ok(undefined, false);

        //cleanup
        _eventResult.noResult(false);
        delete _eventResult;
    } catch (e) {
        result.error("error in stopEvents: " + JSON.stringify(e), false);
    }
},

/**
 * Returns system settings
 * @param success {Function} Function to call if the operation is a success
 * @param fail {Function} Function to call if the operation fails
 * @param args {Object} The arguments supplied
 * @param env {Object} Environment variables
 */
get: function(success, fail, args, env) {
    var result = new PluginResult(args, env)
    try {
        var fixedArgs = _wwfix.parseArgs(args);
        var data = _demo.get();
        result.ok(data, false);
    } catch (e) {
        result.error(JSON.stringify(e), false);
    }
},
    
/**
 * Sets one or more system settings
 * @param success {Function} Function to call if the operation is a success
 * @param fail {Function} Function to call if the operation fails
 * @param args {Object} The arguments supplied
 * @param env {Object} Environment variables
 */
set: function(success, fail, args, env) {
    var result = new PluginResult(args, env)
    try {
        var fixedArgs = _wwfix.parseArgs(args);
        _demo.set(fixedArgs.key, fixedArgs.value);
        result.ok(undefined, false);
    } catch (e) {
        result.error(JSON.stringify(e), false);
    }
}    
In the preceding code, we define four functions:
  • startEvents() and stopEvents(), which use the setTriggerUpdate() function we defined in index.js. This function is referenced as _demo.setTriggerUpdate().
  • get(), which uses the get() function we defined in index.js (referenced here as _demo.get()).
  • set(), which uses the set() function we defined in index.js (referenced here as _demo.set()).

client.js

The client.js file defines the public-facing interface, or client side, of our plugin. This is where we use the cordova.exec() function to call the PPS functions we've defined in our server-side files.

First, we declare our variables. We'll use _ID in our calls to cordova.exec():

var _self = {},
    _ID = "com.qnx.demo",
    _utils = cordova.require('cordova/utils'),
    _watches = {};    

We create a function to hande PPS update events. This function is also passed to cordova.exec(), but isn't accessible to other applications:

/**
 * Handles update events for this plugin
 * @param data {Array} The updated data provided by the event 
 * @private
 */
function onUpdate(data) {
    var keys = Object.keys(_watches);
    for (var i=0; i<keys.length; i++) {
        setTimeout(_watches[keys[i]](data), 0);
    }
}    

Finally, we define our public-facing functions and export them. For each one, we call cordova.exec() and pass it the name of the function to call from index.js:

/**
 * Watch for PPS object changes
 * @param {Function} callback The function to call when a change is detected.
 * @return {String} An ID for the added watch.
 * @example
 * 
 * //define a callback function
 * function myCallback(myData) {
 *        //just send data to log
 *        console.log("Changed data: " , myData);
 *        }
 * }
 * 
 * var watchId = car.demo.watchDemo(myCallback);
 */
_self.watchDemo = function (callback) {
    var watchId = _utils.createUUID();
    
    _watches[watchId] = callback;
    if (Object.keys(_watches).length === 1) {
        window.cordova.exec(onUpdate, null, _ID, 'startEvents', null, false);
    }

    return watchId;
}

/**
 * Stop watching for PPS changes
 * @param {Number} watchId The watch ID as returned by car.demo.watchDemo().
 * @example
 * 
 * car.sensors.cancelWatch(watchId);
 */
_self.cancelWatch = function (watchId) {
    if (_watches[watchId]) {
        delete _watches[watchId];
        if (Object.keys(_watches).length === 0) {
            window.cordova.exec(null, null, _ID, 'stopEvents', null, false);
        }
    }
}

/**
 * Get the value of the demo PPS object
 * @returns {Object} The demo object contents.
 */
_self.get = function () {
        var value = null,
        success = function (data, response) {
            value = data;
        },
        fail = function (data, response) {
            throw data;
        };

    try {
        window.cordova.exec(success, fail, _ID, 'get', null);
    } catch (e) {
        console.error(e);
    }
    return value;
}
    
/**
 * Set a demo object field
 * @param {String} key The data key
 * @param {Mixed} value The data value
 */
_self.set = function (key, value) {
    window.cordova.exec(null, null, _ID, 'set', { key: key, value: value });
}
    
module.exports = _self;  

Applications that use our plugin will need to define a callback function for Cordova to call when watchDemo() is successful.