Switch branches/tags
Nothing to show
Find file History
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


This Template is now available in an improved Yeoman Generator

See: generator-cordova-plugin-bb10

Template for BlackBerry 10 Cordova Plugins

BlackBerry 10 Cordova Plugins create a bridge between JavaScript code in an HTML5 application and C/C++ and/or Qt code in the plugin, allowing access to native libraries, which are not part of the HTML5 specification. The BlackBerry 10 Plugins included with the Cordova SDK also follow the same or similar format as shown in this template.

Plugins offer a JavaScript API which communicates through an interface to native methods. This interface can send and receive data through strings, or data that can be represented as a string such as JSON and Base64.

This template includes examples for communicating in several ways:

  1. A function that returns a string from a native method.
  2. A function that sends a string to, and gets another back, from a native method.
  3. A function that calls a native method with a JSON object input, and provides a callback function, which is fired by a native method, and receives JSON data.
  4. Defining a property that is linked to native methods when retrieved or set.
  5. Starting and Stopping a native thread, with a callback function that receives events from the native thread.

Building and Testing the Sample

RECENT UPDATE: Manually updating the template references can be error prone and hard to debug. An Apache Ant build has been created to setup the Momentics project as well as making the necessary updates to the sample web application. You will still need to follow the process below but will no longer have to update all of the template references as the Apache Ant build will take care of that prior to importing your project into Momentics.

The included sample is a modified version of the default Cordova Hello World application created by the cordova create command. It has been altered to include a div in index.html for displaying the test data, and a set of test functions in js/index.js to excercise the template API and display some results. To add these changes and the plugin to a test project follow these steps:

###Cordova Instructions:

  1. Create a new project using cordova create and,
  2. Copy the www folder in the sample directory over the default one created by that command.
  3. Run the bbndk-env.bat or bbndk-env.sh script if the NDK is not on your PATH.
  4. Add the blackberry10 platform to your project by running cordova platform add blackberry10
  5. Then add the plugin to this new project with cordova plugin add /plugin
  6. Finally, run cordova run

To use the plugin in another project, that's been created with Cordova, run cordova plugin add /plugin

Then you can call the methods with the namespace community.templateplugin, and that should appear in WebInspector as an Object so you can see what APIs are available. The example APIs included will be detailed later in this guide.

This is a screenshot of the test data being displayed in the Hello World sample app: Screenshot

###Prerequisites for building with Apache Ant:

  1. [Apache Ant](http://ant.apache.org/ “Apache Ant”) version 1.9.3
  2. For the current version of Ant, you will also need a JDK installed on your system, version 1.4 or later required, 1.7 or later strongly recommended. The more up-to-date the version of Java , the more Ant tasks you get.

###Building The Template Project: Executing the Apache Ant build will ask you answer a series of questions needed to build a project based on your input:

  • The project name input should be in the following format e.g. HelloWorld. The name should only contain letters
  • The cordova plugin id should be in the format com.blackberry.community.. The name should only contain letters
  • The project author should be entered as the author of the plugin.
  • The build also asks to see if you would like to delete the build directory. Only delete the build directory if you no longer need the project in Momentics. The deletion is not recoverable once y for yes is entered

To build the project execute the following command ant -f build.xml

The output of this build script will produce a build folder that contains two directories:

  1. plugin
  2. sample

Momentics NDK setup

You can either import the project from the Template folder, or use the New Project Wizard in Momentics to create a starter project.

Using the New Project Wizard

  1. Open the Momentics IDE. Navigate to the workbench and from the program menu select File -> New -> New BlackBerry Project.
  2. Choose Native Extension from the Project Type list, then select BlackBerry WebWorks, and click Next.
  3. Change the project name to "Template" and click Finish.
  4. Right click your project again and select Build Configurations -> Manage..., then click New.
  5. Enter "device" for the name and choose to copy settings from Device-Release.
  6. Repeat to create a configuration named "simulator", and choose to copy settings from Simulator-Debug.
  7. You will need to copy the device and simulator folders to the /plugin/src/blackberry10/native directory where you copied the Template, each time you build. You can configure a post build command if you'd like, in the project properties.

Importing the Template

  1. Open the Momentics IDE. Navigate to the workbench and from the program menu select File -> Import and choose "Existing Projects into Workspace".
  2. Choose "Select root directory: " and browse to the /plugin/src/blackberry10/native directory that was just built by Apache Ant. Select the project name you entered in the Apache Ant build in the Projects list and uncheck "Copy projects into workspace". Click Finish.

PLEASE NOTE: Each time the Apache Ant build is run it will ask to delete the build/ directory and recreate a new project based on the downloaded template. Since the instructions are to import the project but not copy the contents into your Momentics workspace only answer y for yes to delete the directory if you no longer need it.

  1. Follow these next steps to build the template plugin to be sure the setup is working.

How to build your native Plugin

  1. Right click your project and select the Clean Project option.
  2. Right click your project again and select Build Configurations -> Build Selected... .
  3. A window will appear that shows all the available build configurations for the project. Select device and simulator and click ok.
  4. You should see the shared libraries (lib.so files e.g. libMMSPlugin.so) generated in the folders for each Build Configuration that you selected.

Using the Plugin in an Application

To use the plugin in another project, that's been created with Cordova, run cordova plugin add /plugin. That will copy the plugin into the project, and update the www/config.xml file to include the feature as below:

<feature name="community.templateplugin" value="community.templateplugin" />

All the methods in the plugin will be prefixed by that feature name, so a method called test() supplied in the community.templateplugin plugin will be called in JavaScript like so:


Depending on the plugin, these methods can also have return values, take arguments, or supply callback methods.

To remove the plugin, run cordova plugin rm community.templateplugin

PLEASE NOTE: You will need to remove and re-add the plugin on each build cycle where you make changes. You can edit the individual files, but they get scattered across various folders. It's safer to edit them in the plugin directory and then do a quick (maybe script it?) cycle of removing and adding the plugin, so the installation process is followed.

Follow the steps above to:

  1. Build the native portion, and
  2. Use the plugin in your test app.
  3. Remember to remove and add the plugin again with the cordova or webworks commands.

Architecture of a Plugin

Plugins are organized into several files, each with a specific role.

  1. plugin.xml - defines the namespace of the plugin lists the files that need to be included.
  2. client.js - the client file defines the API that can be called by an application. It calls to functions in index.js using the Cordova framework. It also connects callback functions to the events that fire them.
  3. index.js - This is the main controller of the plugin. It receives calls from the client.js through Cordova, and calls the appropriate methods in the included JNEXT object, which does the communication to the Native side of JNEXT.
  4. template_js.hpp - C++ header for the JNEXT code. You should rarely have to edit this aside from renaming.
  5. template_js.cpp - C++ code for the JNEXT plugin. Besides renaming, the primary editing of this will be to edit the InvokeMethod function to call the appropriate methods in template_ndk.cpp, given the command and callbackId sent in from index.js and the Template object.
  6. template_ndk.hpp - C++ header for the native code. Where you will need to list method signatures for the extenion methods called from the template_js.cpp file, and any private methods or variables.
  7. template_ndk.cpp - C++ native code. All the main native code will go here typically.

Communication end to end

Plugin methods can be synchronous or asynchronous, send arguments, receive return values, and provide callback methods.

In client.js we define the _ID variable for the name of the plugin, and the exec method from Cordova which will do the communication:

var _self = {},
	_ID = "community.templateplugin",
	exec = cordova.require("cordova/exec");

The basic format of all cordova client-side calls is

exec(success, fail, pluginId, method, args);

In most cases, we'll define success and fail inline.

Synchronous Methods

a simple Synchronous method call looks like this, in client.js:

_self.testInput = function (input) {
	var result,
		success = function (data, response) {
			result = data;
		fail = function (data, response) {
			console.log("Error: " + data);
	exec(success, fail, _ID, "test", { input: input });
	return result;

In index.js, the client-side call maps to

test: function (success, fail, args, env) {
	var result = new PluginResult(args, env);
	args = JSON.parse(decodeURIComponent(args["input"]));
	result.ok(template.getInstance().testInput(result.callbackId, args), false);

Where the success and fail callbacks were sent in from client.js, and args is an object passed in to the exec method, but it has been converted by JSON.stringify() and encoded as a URIcomponent, so you must call


To communicate through Cordova, a new PluginResult is created, and we fire the result.ok method, and pass in the result of the appropriate JNEXT function. In order to handle times where there is a callback, we will always include the callbackId when sending in arguments. So, the method in the JNEXT.Template object looks like the following:

self.testInput = function (callbackId, input) {
	return JNEXT.invoke(self.m_id, "testStringInput " + callbackId + " " + input);

Note: If input is a JSON object, we need to convert it to a string first with JSON.stringify(input).

Now the communication passes into the native layer. JNEXT.invoke eventually calls into:

string TemplateJS::InvokeMethod(const string& command) {
	// format must be: "command callbackId params"
	size_t commandIndex = command.find_first_of(" ");
	std::string strCommand = command.substr(0, commandIndex);
	size_t callbackIndex = command.find_first_of(" ", commandIndex + 1);
	std::string callbackId = command.substr(commandIndex + 1, callbackIndex - commandIndex - 1);
	std::string arg = command.substr(callbackIndex + 1, command.length());
	// ...
	} else if (strCommand == "testStringInput") {
		return m_pTemplateController->templateTestString(arg);

The method name and callbackId are stripped off the front of the command sent in, and the rest is sent in as an argument for the appropriate method. Note the use of the pointer to the TemplateNDK object, so we separate our regular native code from the JNEXT code.

Finally, we call the native method that contains the functionality we are providing to our WebWorks application, and in this example, it takes in the arguments, appends them to a string and returns the result to WebWorks.

// Take in input and return a value
std::string TemplateNDK::templateTestString(const std::string& inputString) {
	return "Template Test Function, got: " + inputString;

If the input is a JSON object, we need to parse it first, as shown here:

// Parse the arg string as JSON
Json::Reader reader;
Json::Value root;
bool parse = reader.parse(inputString, root);

To return JSON, use

Json::FastWriter writer;
Json::Value root;
root["result"] = "result value";
return writer.write(root);

Asynchronous Methods

An Asynchronous method will typically provide a callback function to receive the results of a method, but this is not necessarily the case for all situations. The sequence is much the same as that of Synchronous Methods with these differences:

Asynchronous method call, with both input and a callback function:

_self.testAsync = function (input, callback) {
	var success = function (data, response) {
			var json = JSON.parse(data);
		fail = function (data, response) {
			console.log("Error: " + data);
	exec(success, fail, _ID, "testAsync", { input: input });

In index.js we're going to keep a record of the PluginResult so we can call the callback on it later. And right now, we'll call into JNEXT for the native code, but return noResult(true) right now. The true value tells Cordova to keep our callback around.

testAsync: function (success, fail, args, env) {
	var result = new PluginResult(args, env);
	resultObjs[result.callbackId] = result;
	args = JSON.parse(decodeURIComponent(args["input"]));
	template.getInstance().testAsync(result.callbackId, args);

Now the code proceeds as for Synchronous events, until the template_ndk.cpp method, where instead of returning the result directly, the NotifyEvent method is used instead, but we'll be passing in the callbackId as well this time instead of ignoring it. The callbackId is what we'll use to communicate back through JNEXT:

// Asynchronous callback with JSON data input and output
void TemplateNDK::templateTestAsync(const std::string& callbackId, const std::string& inputString) {
	// Parse the arg string as JSON
	Json::FastWriter writer;
	Json::Reader reader;
	Json::Value root;
	bool parse = reader.parse(inputString, root);

	if (!parse) {
		Json::Value error;
		error["result"] = "Cannot parse JSON object";
		m_pParent->NotifyEvent(callbackId + " " + writer.write(error));
	} else {
		root["result"] = root["value1"].asInt() + root["value2"].asInt();
		m_pParent->NotifyEvent(callbackId + " " + writer.write(root));

This event passes through the JNEXT plugin and is received on the onEvent(strData) method of the JNEXT.Template object. In the case of a standard asynchronous one-time event, we'll lookup the result and call callbackOk(data, false) so the callback is removed from Cordova, and we'll delete our record as well. However, if we need to keep that callback around for further events, we'll need some way of tracking that. In this example, there is only one callback that we need to keep around ever. :

// Fired by the Event framework (used by asynchronous callbacks)
self.onEvent = function (strData) {
	var arData = strData.split(" "),
		callbackId = arData[0],
		result = resultObjs[callbackId],
		data = arData.slice(1, arData.length).join(" ");

	if (result) {
        if (callbackId != threadCallback) {
            result.callbackOk(data, false);
            delete resultObjs[callbackId];
        } else {
        	result.callbackOk(data, true);

result.callbackOk(data, false) will call the success method that we registered at the beginning of the flow, in client.js.

Callbacks for multiple events

The Asynchronous methods above use one-time callbacks to receive their results. Sometimes it is important to receive multiple events on a callback, such as when listening to a thread or longer process.

This is done as above, simply retaining our PluginResult registry until we don't need it any longer, and if necessary, we can return a value immediately but also keep the callback as so:

startThread: function (success, fail, args, env) {
	var result = new PluginResult(args, env);
	if (!threadCallback) {
		threadCallback = result.callbackId;
		resultObjs[result.callbackId] = result;
		result.ok(template.getInstance().startThread(result.callbackId), true);
	} else {
		result.error(template.getInstance().startThread(result.callbackId), false);

At the native layer, the NotifyEvent method can be called multiple times and be received by the callback in this way. The same success method will get called for the immediate result, and all the subsequent callbacks.

Defining Properties

Properties can be defined on the plugin so that code like:


returns a value, and it can be set by the following code:

community.templateplugin.templateProperty = value;

The following code defines a property called templateProperty, backed by the plugin method of the same name for setting or retrieving the value. The getter or setter could be removed to restrict what the property responds to.

Object.defineProperty(_self, "templateProperty", {
	get: function () {
		var result,
			success = function (data, response) {
				result = data;
			fail = function (data, response) {
				console.log("Error: " + data);
		exec(success, fail, _ID, "templateProperty", null);
		return result;
	set: function (arg) {
		var result,
			success = function (data, response) {
				result = data;
			fail = function (data, response) {
				console.log("Error: " + data);
		exec(success, fail, _ID, "templateProperty", {"value": arg });
		return result;

The property calls are the same as synchronous calls, but are adapted to handle both input and no input on the same function in index.js:

templateProperty: function (success, fail, args, env) {
	var result = new PluginResult(args, env);
	var value;
	if (args && args["value"]) {
        value = JSON.parse(decodeURIComponent(args["value"]));
		template.getInstance().templateProperty(result.callbackId, value);
    } else {
        result.ok(template.getInstance().templateProperty(), false);


Debugging plugins is difficult, but there are some ways that can be effective. If you open WebInspector to the first WebView, instead of the second, you will be able to see and interact with the index.js code and the inner parts of the extension. Opening a tab on the second WebView, where you normally would, at the same time will allow you to inspect the whole flow of calls within JavaScript.

Simple JavaScript alerts also work in the plugin files and this can be invaluable to check the value of some data being passed around. Since plugins deal with strings, it's easy to check.

Logging at the Native level with slogger2

You will notice there is a Logger class in the project which has an instance created in template_js.cpp and is used in many of the methods of template_ndk.cpp. This class wraps the slogger2 logging capabilities of BlackBerry 10 for easy logging integration in your Plugins. From your template_ndk.cpp file, you can call these convenience methods to write out a simple string to the log buffer. Appropriate log levels and buffers are created by the Logger class so you don't need to do anything else.

m_pParent->getLog()->debug(const char* message);

m_pParent->getLog()->info(const char* message);

m_pParent->getLog()->notice(const char* message);
m_pParent->getLog()->warn(const char* message);

m_pParent->getLog()->error(const char* message);

m_pParent->getLog()->critical(const char* message);

These methods wrap the slog2c() method which is the fastest logging method, but accepts only character arrays. The Logger creates two different sized buffers: One larger for high frequency, but lower priority messages, and one smaller for low frequency but higher priority messages. The methods above choose the appropriate buffer to use. If you need to send more complicated log messages use the slog2fa() method, and you can access the buffers through these methods:

slog2_buffer_t hiPriorityBuffer();
slog2_buffer_t lowPriorityBuffer();

So, for example, you could do the following:

	#include <slog2.h>
	int some_number = 1;

	slog2fa(m_pParent->getLog()->lowPriorityBuffer(), 0, SLOG2_INFO, "string:%s, some_number:%d", 
		SLOG2_FA_STRING( "Hello world" ), 
		SLOG2_FA_SIGNED( some_number ), 

Slogger2 data can be seen either over an SSH connection to the device, and running slog2info (pass in -w to have the command wait and print out messages as they arrive), or even better in Momentics by right clicking on a Target in the Target Navigator view, and choosing Open Device Log.

Including Libraries

Libraries are added through the Add Library Wizard. Here's how to find it:

  1. Right click on the project, and select properties, then go to C/C++ Build -> Settings.
  2. Choose Tool Settings, and under QCC Linker, select Libraries
  3. Click on Open Add Library Wizard

Add Library

  1. Click Okay to the build settings change.
  2. When prompted choose where the library is that you want to add. For any platform library, you will want the first option.
  3. Find the library in the list. In this example we've searched for and found the QtCore library


  1. Click Next, then Finish.

Now the necessary library paths and include directories should be added. For some Qt based libraries, there are some additional steps.

Special steps for including QtCore and other Qt libraries

If you follow the instructions above and use the Add Library Wizard to add QtCore, there are some additional steps. When you build you'll see the following message:

C:\bbndk10.2\host_10_2_0_15\win32\x86\usr\bin\ntox86-ld: cannot find -lQtCore

Two additional library paths need to be added for the Linker:


Repeat these steps so the same settings exist on both the device and simulator build configurations

Library Paths

Additionally, you should add this include directory for the Compiler Preprocessor:


Repeat these steps so the same settings exist on both the device and simulator build configurations


Common Issues

If you are getting a message saying the application can not load your .so file, it's nearly always a linking problem. Your code may build in the IDE, but not actually link on the device. Make sure you've included all your dependencies and includes properly, on the build that you have loaded. Also, make sure that you've loaded the device build on a device, and the simulator on a simulator.

If your application launches but quickly freezes and then is closed, you may be using a BPS API but haven't called


It is required for things like LED, Vibration, Audio and many others. See more information here.