16. Creating Cordova Plugins

So far we’ve talked a lot about the tools and plugins that are part of the Cordova framework, but what if you want to do something within a Cordova application that isn’t exposed through one of the existing plugins (either core plugins or third-party plugins)? Well, you will have to go it alone and build your own plugins. In this chapter, I show you how to create your own plugins for Apache Cordova.

Cordova plugins are not new. They’ve been around for a long time, but starting with Cordova 3.0 and through the capabilities provided by Plugman and the Cordova CLI, plug-ins changed dramatically. They became more prevalent, plus more standardized in their implementation.

In this chapter, I show you how to create two plugins, a JavaScript-only plugin and a native plugin for multiple target platforms (Android and iOS only, sorry). I will also show how to publish Cordova plugins to the Cordova Plugin Registry.

Anatomy of a Cordova Plugin

Before I jump into how to create a plugin, I thought I’d spend some time explaining the anatomy of a Cordova plugin—what makes a bunch of collected files a Cordova plugin. The Cordova documentation site has great introductory documents that describe how to create plugins. The Plugin Development Guide (http://goo.gl/v1qHBm) describes how to create the JavaScript interface for your plugins, and there are individual guides for creating the native plugin components for the different mobile platforms linked on the bottom of the page. You can find the Android guide at http://goo.gl/hLJukS and iOS at http://goo.gl/byrAD9.

A Cordova plugin is a set of files that together extend or enhance a Cordova application’s capabilities. In general, a developer adds a plugin to a Cordova project (using the tools discussed in Chapter 4, “Using the Cordova Command-Line Interfaces”) and interacts with the plugin through an exposed JavaScript interface. A plugin could do something without any coding by the developer, but in general you add the plugin to your project, then use it as you see fit by coding against the exposed API.

I mentioned that a plugin is a collection of files; it consists of a configuration file called plugin.xml and one or more JavaScript files plus (optionally) some native source code files, libraries, and associated content (HTML, CSS, and other content files) that are used by the plugin.

The plugin.xml describes the plugin and tells the CLI where to copy plugin components in the target mobile application projects. There are even settings in the plugin.xml that are used by the CLI to set platform-specific config.xml settings. There are a lot of available options within the plugin.xml file; I won’t cover all of them here.

A plugin has to have at least one JavaScript source file; this one file is expected to define the methods, objects, and properties that are exposed by the plugin. Your application may wrap everything into one JavaScript file or may have several; it’s all up to you. You can also bundle in additional JavaScript libraries (jQuery Mobile, lawnchair, mustache.js, handlebars.js, and so on) as needed.

Beyond those first two requirements, the rest of the plugin files are anything else you need to define your plugin. In general, a plugin will include one or more native source code files for each supported mobile device platform. On top of that, a plugin might include additional native libraries (in source code form or precompiled) or content (image files, style sheets, HTML files, who knows?).

The good thing about building your own plugins is that there are a whole bunch of examples readily available to you. You can access a plugin’s source code by creating a new Cordova project (using the Cordova CLI, of course), adding several plugins, then opening the project’s plugins folder and poking around at the code within each plugin’s folder. Another option is to look for plugin projects on GitHub (that’s where all of the Cordova core plugins are maintained). Just perform a search using the plugin ID (org.apache.cordova.console, for example), and one of the first few search results should point to the plugin’s code on GitHub.

Creating a JavaScript-Only Plugin

A Cordova plugin does not have to have native code in it; instead, it can be completely composed of JavaScript code. I thought it would make sense to start with a simple plugin, something that shows what a plugin is all about, before digging into plugins with native code; this way you’ll get a feel for the structure of a Cordova plugin without the distraction of native code. I’m calling the plugin I’m creating Meaning of Life, and I will abbreviate it to mol here. In the sections that follow, I’ll expand on what we learned here and build a more complicated example plugin using native code.


Note

The plugin I create here isn’t going to be very useful; it’s merely designed to help teach you about the structure and format of a Cordova plugin. Here I’ll create a plugin that calculates the meaning of life. This will make perfect sense to those of you who are aware of Douglas Adams’s work; for the rest of you, in The Hitchhiker’s Guide to the Galaxy, the “Answer to the Ultimate Question of Life, the Universe, and Everything” (shortened to Meaning of Life by me) is 42, so the one method exposed through the plugin will simply return 42 to the calling program. You can learn more about it at http://goo.gl/Chqm96.


Cordova provides a set of command-line tools for managing plugins called Plugman. I described how to install and use Plugman in Chapter 4. To create this plugin, I opened a terminal window, navigated to the folder where I wanted the plugin created, and issued the following command:

plugman create --name mol --plugin_id com.johnwargo.mol --plugin_version 0.0.1

Plugman will create a folder for the plugin project and populate it with a plugin.xml file and two folders as shown in Figure 16.1. The www folder contains the JavaScript interface files for the plugin, and the src folder is used to host native application code for the plugin. There won’t be any native code for this plugin, so the src folder will go unused in this example.

Image

Figure 16.1 mol Plugin Folder Structure

plugin.xml File

The plugin’s plugin.xml file is a configuration file that a developer uses to describe the plugin as well as the different software components and configuration settings for the plugin. You can read more about the different Cordova configuration files at http://goo.gl/sybkrq. Details about plugins and the plugin.xml file can be found at http://goo.gl/fbDxBw. Details on the Plugin specification are published at http://goo.gl/S4zCXX.

When you create a new plugin, Plugman creates the plugin.xml file for you, then populates it with some of the information passed on the Plugman command line. You can find the complete listing of this plugin.xml file in Listing 16.1.

Listing 16.1 Default plugin.xml File


<?xml version='1.0' encoding='utf-8'?>
<plugin id="com.johnwargo.mol" version="0.0.1"
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
  xmlns:android="http://schemas.android.com/apk/res/android">
    <name>mol</name>
    <js-module name="mol" src="www/mol.js">
        <clobbers target="cordova.plugins.mol" />
    </js-module>
</plugin>


For this particular plugin, I want to add a better description of the plugin as well as my name, so I’ve updated the file and provided the complete listing in Listing 16.2.

Listing 16.2 Updated plugin.xml File


<?xml version='1.0' encoding='utf-8' ?>
<plugin id="com.johnwargo.mol" version="0.0.1"
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
  xmlns:android="http://schemas.android.com/apk/res/android">
  <name>mol</name>
  <description>Answer to the Ultimate Question of Life, the Universe, and Everything
(42)</description>
  <license>Apache 2.0</license>
  <author>John M. Wargo</author>
  <keywords>42</keywords>
  <js-module name="mol" src="www/mol.js">
    <clobbers target="mol" />
  </js-module>
</plugin>


Some of the information in the plugin.xml file is for documentation purposes, to allow others to understand who created the plugin and why. The other options in the file are used to drive the Plugman or the Cordova CLI plugin installation process. You can find a listing of the different plugin.xml elements in Table 16.1.

Image
Image

Table 16.1 Cordova plugin.xml Elements

There are additional supported elements for a plugin’s plugin.xml file; these will be covered when I show how to create a native plugin later in the chapter.

The Plugin’s mol.js File

When Plugman created the plugin project, it also created a base JavaScript file for the plugin. Since the plugin’s name is mol, Plugman created a file called mol.js. This file describes the plugin’s primary JavaScript interface that a Cordova application can call when using the plugin’s capabilities. You can see the contents of the file Plugman created for this plugin in Listing 16.3.

Listing 16.3 Shell mol.js File


var exec = require('cordova/exec'),

exports.coolMethod = function(arg0, success, error) {
    exec(success, error, "mol", "coolMethod", [arg0]);
};


Plugman assumes that the plugin will be executing native code, so it loads the Cordova exec library, then defines an export for a JavaScript method called coolMethod. Inside the exports structure, the code is executing a native method called coolMethod which you would have to provide for each target platform.

For this plugin, I don’t have any native code to write (since this is a JavaScript-only plugin), so everything the plugin needs to do is shown in Listing 16.4. Here I export a function called calculateMOL; the function simply returns the value 42, the answer to the Ultimate Question of Life, the Universe, and Everything (as defined by Douglas Adams).

Listing 16.4 mol.js File


module.exports.calculateMOL = function () {
  return 42;
};


That’s it; that’s all there is to creating a simple, JavaScript-only Cordova plugin.

Now, remember that in the plugin’s plugin.xml file there was this entry:

<js-module name="mol" src="www/mol.js">
  <clobbers target="mol" />
</js-module>

This tells Plugman or the Cordova CLI, when it’s installing the plugin, to copy the mol.js file to each target platform project’s www folder and to expose a mol object that that application can use. To use the plugin, all the application has to do is call mol.calculateMOL() and a value of 42 will be returned.

In this example, I’ve exposed only one method through the plugin. I could easily expose more by simply adding more exports to the mol.js file or adding additional .js files to the project that also export functions. I’ll do this in the next example. You can see an example of how to do this with multiple files in the source code for the Console plugin at http://goo.gl/2zHOOc.

Now, the function I’ve created is not very Cordova-like. It’s a JavaScript plugin and it works, but the majority of Cordova plugins are asynchronous; when a plugin API is called, at least one callback function is passed in, then the function is executed and passed the resulting value. To show you an example of an asynchronous version of the same API, I renamed the existing function calculateMOLSync and added a new calculateMOL to the mol.js file as shown in Listing 16.5.

Listing 16.5 Enhanced mol.js File


module.exports = {

  calculateMOLSync: function() {
    return 42;
  },

  calculateMOL: function(theCallback) {
    theCallback(42);
  }
};


With this in place, an application can call calculateMOLSync() to get the result returned immediately to the application or calculateMOL(callback) to get the result passed to the callback function. I’ll show you an example of how both of these options work in the following section.

Testing the Plugin

To prove that the plugin works, I created a simple MOLTest application to exercise the plugin. To create the project, I opened a terminal window, navigated to my system’s dev folder, then issued the following commands:

cordova create molTest com.ac4p.moltest MOLTest
cd molTest
cordova platform add android browser
cordova plugin add org.apache.cordova.console
cordova plugin add org.apache.cordova.dialogs
cordova plugin add org.apache.cordova.device
cordova plugin add "D:Devpubsac4pchapter 16mol"

I’ve shown the output of most of those commands before (Chapter 4 and Chapter 15, “Cordova Development End to End”), so I won’t show them again. When adding my custom plugin to the project, the CLI displays the following output:

Installing "com.johnwargo.mol" for android
Installing "com.johnwargo.mol" for browser

Notice that I’m adding the browser platform to this project as well. Since the plugin is JavaScript only, I can test this in the browser first before switching to one of the mobile platforms.

I’ve published the plugin to the Cordova Plugin Registry, so you can also add it to your project using the following command:

cordova plugin add com.johnwargo.mol

To see information about all of the installed plugins, issue the following command:

cordova plugins

The CLI will display the following output:

com.johnwargo.mol 0.0.1 "mol"
org.apache.cordova.console 0.2.11 "Console"
org.apache.cordova.device 0.2.12 "Device"
org.apache.cordova.dialogs 0.2.10 "Notification"

With those steps completed, all I had to do was open my HTML editor of choice and write the application. Listing 16.6 shows the application’s index.html file; all that really happens there is that there’s a single button on the page I can tap to check that the plugin is working. The application uses Topcoat to create a more pleasing UI for the application.

Listing 16.6 MOLTest Application index.html File


<!DOCTYPE html>
<html>

<head>
  <title>Meaning of Life Demo</title>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <meta name="viewport" content="user-scalable=no, initial-scale=1,
    maximum-scale=1, minimum-scale=1, width=device-width" />
  <link rel="stylesheet" type="text/css" href="css/topcoat-mobile-light.min.css">
  <script src="index.js"></script>
  <script src="cordova.js"></script>
</head>

<body onload="onBodyLoad()">
  <div class="topcoat-navigation-bar">
    <div class="topcoat-navigation-bar_ _item center full">
      <h1 class="topcoat-navigation-bar_ _title">MoL Demo</h1>
    </div>
  </div>
  <p>Get the answer to the Ultimate Question of Life, the Universe, and Everything
(according to Douglas Adams).</p>
  <button class="topcoat-button--large" onclick="doMOLSync();">Synchronous</button>
  <button class="topcoat-button--large" onclick="doMOLAsync();">Asynchronous</button>
</body>

</html>


The guts of the application are in the project’s index.js shown in Listing 16.7. The file contains the doMOL function that is executed when the user taps the button. The function makes a call to mol.calculateMOL() and displays the result in an alert dialog.

Listing 16.7 MOLTest Application index.js File


function onBodyLoad() {
  console.log("Entering onBodyLoad");
  document.addEventListener("deviceready", onDeviceReady);
  console.log("Leaving onBodyLoad");
}

function onDeviceReady() {
  console.log("Entering onDeviceReady");
  console.log("Cordova: " + device.cordova);
  navigator.notification.alert("Cordova is ready");
  console.log("Leaving onDeviceReady");
}

function doMOLSync() {
  console.log("Entering doMOLSync");
  var res = mol.calculateMOLSync();
  var msg = "Result: " + res;
  console.log(msg);
  navigator.notification.alert(msg, null, "MOL", "Continue");
  console.log("Leaving doMOLSync");
}

function doMOLAsync() {
  console.log("Entering doMOLAsync");
  var res = mol.calculateMOL(molCallback);
  console.log("Leaving doMOLAsync");
}

function molCallback(res) {
  console.log("Entering molCallback");
  var msg = "Result: " + res;
  console.log(msg);
  navigator.notification.alert(msg, null, "MOL", "Continue");
  console.log("Leaving molCallback");
}


That’s all there is to it. When you run the application and tap the Synchronous button, you should see the results shown in Figure 16.2. You should, of course, receive the same result by tapping the Asynchronous button, only the result dialog is displayed by the callback function.

Image

Figure 16.2 Calculating the Answer to the Ultimate Question of Life, the Universe, and Everything on an Android Device

Just so you’ll believe me when I say that this JavaScript-only plugin will work on any supported Cordova device, Figure 16.3 shows the calculation results on the Firefox OS simulator.

Image

Figure 16.3 Calculating the Answer to the Ultimate Question of Life, the Universe, and Everything on a Firefox OS Device

Figure 16.4 shows the results of the calculation on a Windows Phone device.

Image

Figure 16.4 Calculating the Answer to the Ultimate Question of Life, the Universe, and Everything on a Windows Device

Success! Now let me show you a little bit about what happens with Cordova plugins when you add them to a project. Remember from Listing 16.5 that the index.html file has references to the index.js and cordova.js files. However, when you inspect the source code in a debugging tool (like the Chrome Developer Tools shown in Figure 16.5), you’ll see that the index.html file has a bunch of additional script tags added to it. This happens at runtime and is performed by the Cordova container.

Image

Figure 16.5 Chrome Developer Tools Showing the Application’s Modified index.html File

When the container processes its list of plugins on startup, references to each of the plugins’ JavaScript source code are added automatically to the application’s startup file (the index.html in this case and by default). Remember the js-module element in the plugin.xml file? It’s this configuration parameter that tells the Cordova container what file to add and what object it exposes to the application. All of this is done before the Cordova deviceready event is fired by the container and is why it might take a while for that event to fire.

If you look at the mol.js file, you’ll see that it has been modified by the CLI when the plugin is installed in each platform’s plugin folder. As you can see in Figure 16.6, a cordova.define wrapper has been placed around the module.exports shown in Listing 16.5.

Image

Figure 16.6 Modified mol.js File

This extra plumbing is added by the CLI to allow the Cordova container to properly initialize and expose itself to the application running in the container.

Creating a Cordova Native Plugin

Now that I’ve shown you how to create a simple, JavaScript-only plugin, it’s time to go beyond that example and create a plugin for Cordova that contains native code. In the next two sections, I will show you how to create a simple native plugin for Android, then show you how to add the same functionality for iOS. The plugin I’ll be creating isn’t fancy; it simply exposes some native telephony APIs to a Cordova container. For this plugin, I just started poking around in the Android SDK documentation and found something interesting to expose, then coded the plugin. The reason I took this approach was to keep the native API code I was exposing through this plugin as clean as possible. The purpose of this chapter is to show you how to make plugins, not how to do native development, so that’s why the plugin is not too complicated. What you’ll see in the next couple of sections is how to structure your plugin; adding additional APIs or more sophisticated capabilities to your plugins will be easy once you know the basics of how native plugins work.

The plugin I’ll be creating is called Carrier, and it exposes information provided by a smartphone’s carrier: the carrier name plus the country code where the device is located or provisioned.

To create the plugin, I opened a terminal window, navigated to the folder where I wanted to create the plugin project, and issued the following commands:

plugman create --name carrier --plugin_id com.johnwargo.carrier
--plugin_version 0.0.1

Remember, the plugin is called Carrier, so I rightfully named the plugin project folder carrier as well.

As you saw with the JavaScript plugin, Plugman will create a project structure for the plugin, populating it with a simple plugin.xml file, two folders, and a shell JavaScript file that provides the API interface for the plugin.

Since I know I will be working with native code, I can use Plugman to add support for a limited number of platforms (only Android and iOS today unfortunately). To add support for any other platforms, you will need to create the necessary platform folder, populate the folder with source code files, and update the plugin.xml file with settings for the platform. This is surprising considering Plugman is more than a year old, but it is what it is.

To add support for Android and iOS, in the terminal window, execute the following commands:

cd carrier
plugman platform add --platform_name android
plugman platform add --platform_name ios

Here I’m changing into the plugin project folder that was just created and adding the two platforms individually. Remember from Chapter 4 that the Cordova CLI allows you to add multiple platforms with one command; unfortunately that doesn’t work for Plugman. At the conclusion of the process, the plugin project’s src folder will have two additional folders in it as shown in Figure 16.7. It’s not shown here, but the android and ios folders shown in the figure have also been populated with the shell source code files needed for the plugin.

Image

Figure 16.7 Carrier Plugin Folder Structure

In Listing 16.1 I showed the default plugin.xml file created by Plugman. Now that I’ve added two platforms to the plugin project, the plugin.xml file looks like what is shown in Listing 16.8. There are some problems with this file, but I’ll fix them a little later and show you the results.

Listing 16.8 Carrier Plugin plugin.xml File


<?xml version='1.0' encoding='utf-8' ?>
<plugin id="com.johnwargo.carrier" version="0.0.1"
  xmlns=http://apache.org/cordova/ns/plugins/1.0
  xmlns:android="http://schemas.android.com/apk/res/android">
  <name>carrier</name>
  <js-module name="carrier" src="www/carrier.js">
    <clobbers target="cordova.plugins.carrier" />
  </js-module>
  <platform name="android">
    <config-file parent="/*" target="res/xml/config.xml">
      <feature name="carrier">
        <param name="android-package"
          value="com.johnwargo.carrier.carrier" />
      </feature>
    </config-file>
    <config-file parent="/*" target="AndroidManifest.xml" />
    <source-file src="src/android/carrier.java"
      target-dir="src/com/johnwargo/carrier/carrier" />
  </platform>
  <platform name="ios">
    <config-file parent="/*" target="config.xml">
      <feature name="carrier">
        <param name="ios-package" value="carrier" />
      </feature>
    </config-file>
    <source-file src="src/ios/carrier.m" />
  </platform>
</plugin>


In the project’s www folder is a JavaScript file named for the plugin project, carrier.js in this case. The contents of the file are the same as what was shown in Listing 16.3.

Before I start on the native code, I need to first define the JavaScript interface that will be exposed to Cordova applications. The way this works is through the same type of JavaScript file I created for the mol plugin in the previous section. In the JavaScript file I created for this plugin I created a simple JavaScript object called carrier and defined within it one or more methods that can be called from a Cordova application. For this particular example, the Carrier plugin will expose the getCarrierName and getCountryCode methods.

Now, unlike the mol plugin, these JavaScript methods will not do any calculations and return any values directly; instead, they will make calls to a cordova.exec method that passes control to native code I’ve created for each target platform. This is the famous JavaScript to native bridge that allows Cordova applications to execute native APIs. When the native code is finished doing what it is supposed to be doing, it executes callback functions provided by the JavaScript code and passes back the results to the JavaScript code.

The method signature for cordova.exec is

cordova.exec(successCallback, errorCallback, 'PluginObject',
  'pluginMethod', [arguments]);

The successCallback and errorCallback parameters are the names of the functions that will be executed on success and failure of the particular plugin method call. The 'PluginObject' parameter is a string that identifies the native object that contains the method being called, and the 'pluginMethod' parameter is a string that identifies the method of the object that is executed. Last, the arguments parameter is an optional array of arguments that will be passed to the pluginMethod for it to use when performing whatever task it is performing.

In this example, the getCarrierName and getCountryCode methods don’t require that any parameters be passed to them, so the arguments parameter will be empty and represented by empty brackets: [].

Listing 16.9 shows the contents of the project’s carrier.js file, the JavaScript interface file for the plugin. This file was created by Plugman; I simply adjusted it for the plugin. The code begins by defining the Cordova exec object by loading a code library through require. When this is done, the exec object can be used to invoke the JavaScript to native bridge. This library is a core part of the Cordova container and should be available to any Cordova plugin automatically.

With that in place, the carrier object is created and the two methods are defined. Each method calls cordova.exec and passes the necessary parameters (function names and object and method names) needed for the bridge to operate.

At the very end of the file is a module.exports assignment that allows the carrier object to be exposed to a Cordova application.

Listing 16.9 Carrier Plugin carrier.js File


var exec = require('cordova/exec'),

var carrier = {
  getCarrierName: function (successCallback, errorCallback) {
    exec(successCallback, errorCallback, 'carrier', 'getCarrierName', []);
  },

  getCountryCode: function (successCallback, errorCallback) {
    exec(successCallback, errorCallback, 'carrier', 'getCountryCode', []);
  }
};

module.exports = carrier;


Let’s take a look at one of the method definitions:

getCarrierName: function (successCallback, errorCallback) {
  exec(successCallback, errorCallback, 'carrier', 'getCarrierName', []);
},

The getCarrierName in the first line of the code defines one of the JavaScript methods exposed by the plugin. The method takes two parameters: success and error callback functions. If the method needed any additional parameters to operate, you would define them after the errorCallback parameter.


Note

Do you have to define the callback functions first and in that order followed by any additional parameters? No, not really. However, think about all of the Cordova APIs you experienced in Chapter 14, “Working with the Cordova APIs.” Structuring an API method this way is the default way Cordova APIs are defined, so if you want your plugin to “feel” like the core Cordova plugins, you’ll want to follow this convention.


As you can see from the code, all of the parameters are simply passed on to the Cordova JavaScript to native bridge through the execution of the exec method. The method signature for exec is fixed; if your plugin needs to pass additional parameters to the native code, you must pass them through the arguments parameter, the empty brackets [] shown on the exec line.

With that in place I am ready to begin coding the native parts of the plugin.


Note

You don’t have to define the JavaScript interface for your plugin first. If you look at the different Cordova plugin tutorials available on the Internet, you will find that people do it either way: define the JavaScript interface first or write the native code first. It really doesn’t matter. I researched the required native functions first, so I already had what I needed to craft the JavaScript interface.

If you are working with a more complicated plugin, you may find it easier to work through all of the native functions and what they will require before crafting your plugin’s JavaScript interface.


Creating the Android Plugin

With the JavaScript interface defined, it’s time to start working on the native part of the plugin. In this case, I did things alphabetically, so I created the Android plugin first, then ported it to iOS later.

On Android, the information the plugin will be returning to the Cordova application will come from the Telephony API. To use this API, an application must import the Context and TelephonyManager classes:

import android.content.Context;
import android.telephony.TelephonyManager;

Then, the code must define an instance of the TelephonyManager class that exposes the methods we need to call to get the carrier name and country code:

tm = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

To determine the carrier name, the plugin will make a call to tm.getSimOperatorName(), and to get the country code, it will make a call to tm.getSimCountryIso().

Listing 16.10 lists the Java code used for the Android plugin; it defines a simple class called CarrierPlugin that exposes the exec method that is executed by the call to the JavaScript cordova.exec in Listing 16.9.

Listing 16.10 Carrier Plugin carrier.java File


package com.johnwargo.carrier;

//Cordova imports
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CordovaWebView;

//Android imports
import android.content.Context;
import android.telephony.TelephonyManager;

//JSON Imports
import org.json.JSONArray;
import org.json.JSONException;

public class carrier extends CordovaPlugin {

  // define some constants for the supported actions
  public static final String
    ACTION_GET_CARRIER_NAME = "getCarrierName";
  public static final String
    ACTION_GET_COUNTRY_CODE = "getCountryCode";

  public TelephonyManager tm;

  public void initialize(CordovaInterface cordova,
    CordovaWebView webView) {
    super.initialize(cordova, webView);
    // the plugin doesn't have direct access to the application
    // context, so you have to get it first
    Context context = this.cordova.getActivity().getApplicationContext();
    // Next we initialize the tm object
    tm = (TelephonyManager) context
      .getSystemService(Context.TELEPHONY_SERVICE);
  }

  @Override
  public boolean execute(String action, JSONArray args,
    CallbackContext callbackContext) throws JSONException {
    try {
      // First check on the getCarrierName
      if (ACTION_GET_CARRIER_NAME.equals(action)) {
        callbackContext.success(tm.getSimOperatorName());
        return true;
      } else {
        // Next see if it is a getCountryCode action
        if (ACTION_GET_COUNTRY_CODE.equals(action)) {
          callbackContext.success(tm.getSimCountryIso());
          return true;
        }
      }
      // We don't have a match, so it must be an invalid action
      callbackContext.error("Invalid Action");
      return false;
    } catch (Exception e) {
      // If we get here, then something horrible has happened
      System.err.println("Exception: " + e.getMessage());
      callbackContext.error(e.getMessage());
      return false;
    }
  }
}


The class defines two constants, ACTION_GET_CARRIER_NAME and ACTION_GET_COUNTRY_CODE, that are used in determining which method was called by the Cordova application. You could hard-code the comparison deeper within the Java code, but doing it this way makes it easier to change the names later.

Next, the class defines the tm object used to give the plugin access to the Telephony APIs.

The initialize method first calls super.initialize, which allows the cordova object to be initialized properly. Without this call, the Java code has no awareness of the Cordova container. The code then gets a handle to the current application context and uses that to wire the tm object into the services exposed by the Telephony API.

Next, the Java code overrides the exec method and implements the code that deals directly with the calls from the Cordova application. In this implementation, I’ve implemented a single operation that determines which action has been called (by comparing the action name passed by the call to cordova.exec to the constants I defined earlier) and acts accordingly.

If the exec method determines that the getCarrierName action was requested, it makes the call to the Android getSimOperatorName method and passes the results back to the Cordova application by calling callbackContext.success(). If the getCountryCode action was requested, it makes a call to the Android getSimCountryIso() and passes the results back to the Cordova application by calling callbackContext.success().

If any part of this process fails, the code executes callbackContext.error and passes back an appropriate error message or error object indicating what went wrong.


Note

I could have done more error checking and provided more ways for the plugin to let the calling application know when things went wrong. Remember, though, I’m trying to demonstrate how to write a Cordova plugin, not highlight all the things you can do in native code. Be sure to spend some time with the source code of any of the Cordova core or third-party plugins; you’ll see a lot more of what you can do in a plugin.


That’s all there is to the code; I tried to make it as simple as possible in order to allow you to focus on the parts that are specific to Cordova plugins.

Before I can use the CLI to add this new plugin to a Cordova project, I have to update the plugin’s plugin.xml with some additional information. Listing 16.11 shows the complete file contents for this plugin (so far, I’m omitting the iOS components and will cover them later). I’ve highlighted in bold the parts of the file that are different from the file created by Plugman (shown in Listing 16.8).

Listing 16.11 plugin.xml


<?xml version='1.0' encoding='utf-8' ?>
<plugin id="com.johnwargo.carrier" version="0.0.1"
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
  xmlns:android="http://schemas.android.com/apk/res/android">
  <name>carrier</name>
  <description>A Cordova Plugin for retrieving Wireless Carrier information.</
description>

  <author>John M. Wargo</author>
  <keywords>carrier,telephony</keywords>
  <js-module name="carrier" src="www/carrier.js">
    <clobbers target="carrier" />
  </js-module>
  <platform name="android">
    <config-file parent="/*" target="res/xml/config.xml">
      <feature name="carrier">
        <param name="android-package"
          value="com.johnwargo.carrier.carrier" />
      </feature>
    </config-file>
    <config-file parent="/*" target="AndroidManifest.xml" />
    <source-file src="src/android/carrier.java"
      target-dir="src/com/johnwargo/carrier" />
    <config-file target="AndroidManifest.xml" parent="/*">
      <uses-permission
        android:name="android.permission.READ_PHONE_STATE" />
    </config-file>
  </platform>
  <platform name="ios">
    <!—some iOS Stuff I'll add and explain later-->
  </platform>
</plugin>


The file’s js-module element defines the name of the JavaScript file that will be automatically loaded on application startup. It defines the JavaScript interface exposed to the Cordova application. The clobbers element specifies the JavaScript object that is assigned to the loaded JavaScript object. In this example, shown here, I am directing that the Carrier plugin be exposed to the Cordova application through a carrier object:

<js-module name="carrier" src="www/carrier.js">
  <clobbers target="carrier" />
</js-module>

The Cordova application will access the getCarrierName method through the carrier object as shown here:

carrier.getCarrierName(onSuccess, onFailure);

In the plugin.xml file created by Plugman, the clobbers target was defined as cordova.plugins.carrier, so to use the plugin in a Cordova application, I would have had to use

cordova.plugins.carrier.getCarrierName(onSuccess, onFailure);

which is not simple enough for my needs.

The other part of the file that is different from the earlier example (Listing 16.1) is the platform section shown here:

<platform name="android">
  <config-file parent="/*" target="res/xml/config.xml">
    <feature name="carrier">
      <param name="android-package"
        value="com.johnwargo.carrier.carrier" />
    </feature>
  </config-file>
  <config-file parent="/*" target="AndroidManifest.xml" />
  <source-file src="src/android/carrier.java"
    target-dir="src/com/johnwargo/carrier" />
  <config-file target="AndroidManifest.xml" parent="/*">
    <uses-permission
      android:name="android.permission.READ_PHONE_STATE" />
  </config-file>
</platform>

It defines settings that are particular to specific mobile device platforms and contains settings that relate to the native code I’ve shown you in this section. There can be one or more platform elements in a plugin.xml file; the one shown next defines elements that apply to Android plugin components:

<platform name="android"></platform>

Within that element is a source-file element that points to one or more Android native source files that need to be installed by the CLI when the plugin is installed. In the following example, it is instructing Plugman or the CLI to copy the file called carrier.java located in the plugin source folder’s src/android folder to the Cordova project’s Android platform folder in the src/com/johnwargo/carrier folder:

<source-file src="src/android/carrier.java"
  target-dir="src/com/johnwargo/carrier" />

You don’t have to copy the file to that deep of a folder structure, but it’s the norm for Cordova plugins to be installed into a folder structure like that. Take a look at some of the Cordova core plugins and you will see what I mean.

The config-file element defines the changes that need to be made during plugin installation. In the following example, it is specifying that a feature named carrier should be added to the Android project’s config.xml file and point to the Java class com.johnwargo.carrier.carrier:

<config-file parent="/*" target="res/xml/config.xml">
  <feature name="carrier">
    <param name="android-package"
      value="com.johnwargo.carrier.carrier" />
  </feature>
</config-file>

The last element in platform defines another configuration file setting. On Android, access to the Telephony API requires specific permissions. Any application that uses the API must add an entry to the application’s AndroidManifest.xml file that lists the specific permissions required by the application. In this case, I have to add the android.permission.READ_PHONE_STATE permission to the manifest as shown here:

<config-file target="AndroidManifest.xml" parent="/*">
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
</config-file>

When you look at the settings screen for an application that uses this plugin, you will see the entry for “read phone status and identity” shown in Figure 16.8; that shows that the permission has been set correctly.

Image

Figure 16.8 Android Application Permissions

If you do not set these permissions correctly, the part of the application that uses an API that requires the permission will fail without warning. If you forget to do this and wonder later why the Telephony API doesn’t seem to be working, be sure to check the application permissions.

To test the application, I created the simple application shown in Listing 16.12. To create the application, I opened a terminal window, navigated to my system’s dev folder, then issued the following commands:

cordova create carriertest com.ac4p.carriertest CarrierTest
cd carriertest
cordova platform add android
cordova plugin add c:devpluginscarrier

I’ll show you later how to publish the plugin to the Cordova Plugin Registry, but to use the published version of the plugin, you could add it to the project using

cordova plugin add com.johnwargo.carrier

With those steps completed, all I had to do was open my HTML editor of choice and enter the code shown in Listing 16.12. The application displays two buttons, one that makes a call to getCarrierName and another that makes a call to getCountryCode. The same onSuccess function is executed to display the results for both methods.

Listing 16.12 Carrier Plugin index.html


<!DOCTYPE html>
<html>

<head>
  <title>Carrier Demo</title>
  <meta http-equiv="Content-type" content="text/html; charset=utf-8">
  <meta name="viewport" content="user-scalable=no, initial-scale=1,
    maximum-scale=1, minimum-scale=1, width=device-width" />
  <link rel="stylesheet" type="text/css" href="css/topcoat-mobile-light.min.css">
  <script src="index.js"></script>
  <script src="cordova.js"></script>
</head>

<body onload="onBodyLoad()">
  <div class="topcoat-navigation-bar">
    <div class="topcoat-navigation-bar_ _item center full">
      <h1 class="topcoat-navigation-bar_ _title">Carrier Demo</h1>
    </div>
  </div>
  <p>This is a Cordova application that uses my fancy new Carrier plugin.</p>
  <div class="topcoat-button-bar">
    <div class="topcoat-button-bar_ _item">
      <button class="topcoat-button-bar_ _button--large"
        onclick="doGetCarrier();">Carrier Name</button>
    </div>
    <div class="topcoat-button-bar_ _item">
      <button class="topcoat-button-bar_ _button--large"
        onclick="doGetCountryCode();">Country Code</button>
    </div>
  </div>
</body>

</html>


The application uses the Topcoat CSS framework (http://topcoat.io) to give the application a cleaner UI; I’ll describe Topcoat in Chapter 17, “Using Third-Party UI Frameworks with Cordova.”

The application’s JavaScript code is split out into the index.js file shown in Listing 16.13.

Listing 16.13 Carrier Plugin index.js


function onBodyLoad() {
  console.log("Entering onBodyLoad");
  document.addEventListener("deviceready", onDeviceReady);
  console.log("Leaving onBodyLoad");
}

function onDeviceReady() {
  console.log("Entering onDeviceReady");
  console.log("Cordova: " + device.cordova);
  navigator.notification.alert("Cordova is ready");
  console.log("Leaving onDeviceReady");
}

function doGetCarrier() {
  console.log("Entering doGetCarrier");
  carrier.getCarrierName(onSuccess, onError);
  console.log("Leaving doGetCarrier");
}

function doGetCountryCode() {
  console.log("Entering doGetCountryCode");
  carrier.getCountryCode(onSuccess, onError);
  console.log("Leaving doGetCountryCode");
}

function onSuccess(res) {
  console.log("Entering onSuccess");
  var msg = "Result: " + res;
  console.log(msg);
  navigator.notification.alert(msg, null, "Carrier Information", "Continue");
  console.log("Leaving onSuccess");
}

function onError(err) {
  console.log("Entering onError");
  console.error(JSON.stringify(err));
  var msg = "Error obtaining carrier information: " + err;
  navigator.notification.alert(msg, null, "Carrier Error", "Oops");
  console.log("Leaving onError");
}


When the application runs, it will display a screen similar to the one shown in Figure 16.9 (which I have cropped here for the sake of page real estate).

Image

Figure 16.9 Carrier Demo Application

As you know, I like to start testing Cordova applications using simulators first. So, when I fired up this application on an Android emulator and tapped the Carrier Name button, the application displayed the message shown in Figure 16.10. Since the emulator isn’t provisioned with a wireless carrier, the API doesn’t have anything useful to return, so it simply returns “Android.”

Image

Figure 16.10 Results of getCarrierName on an Android Emulator

Now, I have an Android tablet, a Nexus 7, that is cellular enabled, but it’s not a phone; Figure 16.11 shows what happens when I try to determine the provisioned carrier on the device. Unfortunately, since the tablet is not a phone, the Android Telephony APIs aren’t going to help me. So, for a production plugin, not this simple sample I’ve created here, I’ll have to check for a blank return value and return a message indicating that the device isn’t telephony enabled.

Image

Figure 16.11 Results of getCarrierName on an Android Tablet

And finally to my Android smartphone. When I tap the Carrier Name button, the application will call the appropriate Telephony API and return my carrier’s name as shown in Figure 16.12.

Image

Figure 16.12 Results of getCarrierName on an Android Smartphone

When I tap the Country Code button, the device shows the message shown in Figure 16.13.

Image

Figure 16.13 Results of getCountryCode on an Android Device

Clearly I have some work to do to make my plugin deal correctly with devices that aren’t phones or aren’t provisioned with a wireless carrier, but at least I now have a native plugin for Android and it’s all ready to go.

Creating the iOS Plugin

In this section, I will show you how to implement the iOS version of the native Carrier plugin. For the iOS version of the plugin, things are a little simpler. On Android, when you call a plugin’s methods, cordova.exec locates the native class and executes the methods on the class. For iOS, the plugin’s methods are executed through URL commands exposed by the plugin.

When I created the plugin and added the iOS platform to the project, Plugman created a shell source code file for me to use for my plugin. The file is called carrier.h and it’s located in the project’s src/ios folder. The contents of the file are shown in Listing 16.14.

Listing 16.14 Carrier Plugin carrier.m File


/********* carrier.m Cordova Plugin Implementation *******/

#import <Cordova/CDV.h>

@interface carrier : CDVPlugin {
  // Member variables go here.
}

- (void)coolMethod:(CDVInvokedUrlCommand*)command;
@end

@implementation carrier

- (void)coolMethod:(CDVInvokedUrlCommand*)command
{
    CDVPluginResult* pluginResult = nil;
    NSString* echo = [command.arguments objectAtIndex:0];

    if (echo != nil && [echo length] > 0) {
        pluginResult = [CDVPluginResult
          resultWithStatus:CDVCommandStatus_OK messageAsString:echo];
    } else {
        pluginResult =
          [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR];
    }

    [self.commandDelegate sendPluginResult:pluginResult callbackId:command.
callbackId];
}

@end


The code imports the Cordova header file called CDV.h, defines an interface for the coolMethod method, then defines the implementation for the carrier object exposed through the plugin. If you study the code, you’ll see that it’s showing how to process command arguments and return results for success and failure.

For this plugin, I need to define two methods to accommodate the getCarrierName and getCountryCode JavaScript functions the plugin exposes. In Listing 16.15, I’ve updated the file with the code we need for the iOS implementation of the plugin.

Listing 16.15 Updated Carrier Plugin carrier.m File


#import <Cordova/CDV.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>

@interface carrier : CDVPlugin {
  // Member variables go here.

}

- (void)getCarrierName:(CDVInvokedUrlCommand*)command;
- (void)getCountryCode:(CDVInvokedUrlCommand*)command;

@end

@implementation carrier

- (void)getCarrierName:(CDVInvokedUrlCommand*)command
{
    CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
    CTCarrier *carrier = [netinfo subscriberCellularProvider];
    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[carrier carrierName]];
    [self.commandDelegate sendPluginResult:result callbackId:[command callbackId]];
}

- (void)getCountryCode:(CDVInvokedUrlCommand*)command
{
    CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
    CTCarrier *carrier = [netinfo subscriberCellularProvider];
    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[carrier isoCountryCode]];
    [self.commandDelegate sendPluginResult:result callbackId:[command callbackId]];
}

@end


First, the code imports the library files the plugin needs to access the iOS Telephony APIs:

#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>

Next, the code defines the carrier interface as an implementation of CDVPlugin. What this does is let the iOS device know I’m exposing the code as a Cordova plugin. Next, the file defines the two methods used by the plugin: getCarrierName and getCountryCode. These are the methods that will be called by the plugin’s JavaScript interface shown in Listing 16.9.

As you can see from the code, both methods leverage CTTelephonyNetworkInfo; for this plugin, neither of the methods accepts any parameters. For both methods, the plugin first defines a netinfo object that is assigned to an instance of the CTTelephonyNetworkInfo class as shown here:

CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];

Next, a carrier object is created that provides the plugin with access to the cellular provider properties as shown here:

CTCarrier *carrier = [netinfo subscriberCellularProvider];

With that in place, the carrier name is exposed to the plugin through the carrier object’s carrierName property. At this point, the plugin simply needs to return the value to the calling program. First the method sets the result of the operation and assigns the return value as shown here:

CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
messageAsString:[carrier carrierName]];

Then the method calls the appropriate callback function to complete the process:

[self.commandDelegate sendPluginResult:result callbackId:[command callbackId]];

That’s it; that’s all there really is to the code. For the getCountryCode method, it uses the same process, only returning the value of the carrier object’s isoCountryCode property.

With the plugin’s code in place, the last thing that has to happen is to update the plugin’s plugin.xml file with the settings for the iOS portion of the plugin. To do this, I added a new platform section to the file; the elements and attributes for this section are shown here:

<platform name="ios">
  <config-file parent="/*" target="config.xml">
    <feature name="carrier">
      <param name="ios-package" value="carrier" />
    </feature>
  </config-file>
  <source-file src="src/ios/carrier.m" />
</platform>

This content defines the carrier feature that the plugin uses. It also specifies the source-file for the project, the carrier.m file shown in Listing 16.15.

Note that there are no permission settings to be set for this plugin; iOS doesn’t require specific application settings to use telephony features as Android does. At this point I have everything defined that I need for the plugin. Listing 16.16 shows the complete listing for the plugin.xml file.

Listing 16.16 Complete plugin.xml File


<?xml version='1.0' encoding='utf-8' ?>
<plugin id="com.johnwargo.carrier" version="0.0.1"
  xmlns="http://apache.org/cordova/ns/plugins/1.0"
  xmlns:android="http://schemas.android.com/apk/res/android">
  <name>carrier</name>
  <description>A Cordova Plugin for retrieving Wireless Carrier information.</
description>
  <author>John M. Wargo</author>
  <keywords>carrier,telephony</keywords>
  <js-module name="carrier" src="www/carrier.js">
    <clobbers target="carrier" />
  </js-module>
  <platform name="android">
    <config-file parent="/*" target="res/xml/config.xml">
      <feature name="carrier">
        <param name="android-package"
          value="com.johnwargo.carrier.carrier" />
      </feature>
    </config-file>
    <config-file parent="/*" target="AndroidManifest.xml" />
    <config-file target="AndroidManifest.xml" parent="/*">
      <uses-permission
        android:name="android.permission.READ_PHONE_STATE" />
    </config-file>
    <source-file src="src/android/carrier.java" target-
dir="src/com/johnwargo/carrier" />
  </platform>
  <platform name="ios">
    <config-file parent="/*" target="config.xml">
      <feature name="carrier">
        <param name="ios-package" value="carrier" />
      </feature>
    </config-file>
    <source-file src="src/ios/carrier.m" />
  </platform>
</plugin>


To test the plugin, I used my existing test application and added the iOS platform to it. When I opened Xcode and tried to run the application, I received an error indicating that there was an undefined symbol in my project as shown in Figure 16.14.

Image

Figure 16.14 Xcode Build Error

This happens because the project didn’t have the iOS CoreTelephony framework added to the project. To fix this, I had to click on the project name in the Xcode project navigator, open the Build Phases tab, and add the framework to the Link Binary With Libraries section as shown already completed in Figure 16.15.

Image

Figure 16.15 Xcode Project Build Phases Settings

To fix this, I clicked the plus sign highlighted at the bottom of Figure 16.15 and in the dialog that appeared, I started to type telephony as shown in Figure 16.16. When the CoreTelephony.framework entry appeared as shown in the figure, I clicked the Add button to add the framework to my project’s configuration.

Image

Figure 16.16 Xcode Add Framework Dialog

That’s an ugly solution, isn’t it? You can fix this for your users and keep them from seeing errors generated by the plugin’s use of the Telephony APIs by adding the following line to the iOS portion of the plugin’s plugin.xml file:

<framework src="CoreTelephony.framework" />

This configures the project with the right libraries, and the project should build without error.

With that in place, I copied over my test project’s index.html and ran the application on an iOS simulator as shown in Figure 16.17.

Image

Figure 16.17 Results of getCarrierName on an iOS Simulator

Of course, since the simulator isn’t provisioned for any carrier, the API doesn’t know what to return and simply returns null. I get the same results for country code on the simulator.

When I run the application on my iOS device, I get the expected results shown in Figure 16.18.

Image

Figure 16.18 Results of getCarrierName on an iOS Device

When I try to get the country code on my iOS device, the plugin returns null, because I don’t have a SIM in the device. When I try this on my iPad, which does have a SIM, I get the results shown in Figure 16.19.

Image

Figure 16.19 Results of getCountryCode on an iOS Device

If I were working with a production-grade plugin, I would want to tune the native implementations for Android and iOS so they deliver consistent results on the simulators. I could have coded the plugin’s methods so they call the onFailure callback function and pass back an error message indicating that the carrier information was not available. Perhaps that is an enhancement you can make to the plugin.

Publishing Plugins

When it comes to deploying a custom plugin, you have several options. You can zip up the plugin folder, the folder containing your plugin’s plugin.xml file plus all of the source files for the plugin, and distribute the .zip file to your users. All the user would have to do is extract the files into a folder and point to that folder when doing a cordova plugin add from a terminal window.

Cordova plugins, however, are best deployed through the Cordova Plugin Registry (CPR) at http://plugins.cordova.io. This is how the core Cordova plugins are deployed, and it’s the place where most Cordova developers will expect to find your plugin. Fortunately it’s pretty easy to publish plugins to the registry using Plugman.

To publish a plugin, you must first define a CPR user using the following:

plugman adduser

Plugman will prompt you for your username, password, and email address, then create an account for you on the registry.

Before you publish your plugin, you should first provide some documentation for the plugin and include it with the plugin’s files. This is accomplished through the project’s readme.md file. The .md extension for the file refers to the Markdown format (the opposite of markup) described at http://goo.gl/aVJGuS. The file should describe the plugin and list each method and property exposed by the plugin and any additional information users will need to know before they can use the plugin in their own projects. You can see an example of this in Figure 16.20; in the figure I show the plugin’s readme in MarkdownPad, an editor (with preview) for Markdown files.

Image

Figure 16.20 MarkdownPad: Carrier Plugin readme.md File

With the readme file in place, publish the plugin to the registry by opening a terminal window and executing the following:

plugman publish pluginfolder

In this example, the pluginfolder parameter is required; Plugman doesn’t seem to know how to publish the plugin from the current folder.

So, if the terminal window is sitting right above the carrier plugin folder (called carrier), I could publish the plugin using

plugman publish carrier

If the terminal window is pointing to the plugin folder, you can publish the plugin to the registry using

plugman publish .

Plugman will update the terminal window as it publishes the plugin as shown in Figure 16.21.

Image

Figure 16.21 Plugman: Publishing a Plugin

When Cordova 3 was first released, the Cordova team used GitHub to store plugins. The team recently moved away from GitHub to NPM (http://npmjs.org), so that’s where the Cordova core plugins can be found, and the CPR stores plugins there as well.

During the publishing process, Plugman first creates a package.json file for the plugin using information in the plugin.xml file, publishes the plugin to NPM, deletes the package.json file, and updates the entry in the registry. If a problem is encountered with publishing the plugin, you may find the package.json file left lying around in your plugin folder.

Once the plugin has been published, you can view the plugin information in the registry as shown in Figure 16.22.

Image

Figure 16.22 Cordova Plugin Registry: Carrier Entry

With the plugin published, all of the Cordova CLI and Plugman plugin-related commands work. For example, you can now add this plugin to your projects using the plugin ID:

cordova plugin add com.johnwargo.carrier

You can search for plugins using keywords:

cordova plugin search carrier

The CLI will return the following:

com.johnwargo.carrier - A Cordova Plugin for retrieving Wireless Carrier
information.
com.rensanning.cordova.carrier - CarrierPlugin Description

Notice that there are two plugins with the keyword carrier.

You can even view plugin information from the command line using

plugman info com.johnwargo.carrier

The CLI will return the following:

{ '0.0.3':
   { _id: '[email protected]',
     _rev: '7-ab70bc82d8aa9c00d7aa1b7421cc3ee0',
     name: 'com.johnwargo.carrier',
     description: 'A Cordova Plugin for retrieving Wireless Carrier information.',
     'dist-tags': { latest: '0.0.3' },
     versions: [ '0.0.1', '0.0.2', '0.0.3' ],
     maintainers: [ [Object] ],
     time:
      { '0.0.1': '2014-11-25T12:31:38.708Z',
        '0.0.2': '2014-11-25T12:36:34.811Z',
        '0.0.3': '2014-11-25T17:08:22.601Z' },
     _attachments:
      { 'com.johnwargo.carrier-0.0.3.tgz': [Object],
        'com.johnwargo.carrier-0.0.2.tgz': [Object],
        'com.johnwargo.carrier-0.0.1.tgz': [Object] },
     version: '0.0.3',
     cordova_name: 'carrier',
     keywords: [ 'carrier', 'telephony' ],
     platforms: [ 'android', 'ios' ],
     engines: [],
     readmeFilename: 'readme.md',
     dist:
      { shasum: '1c091a315f25b59fd972016614318ce8c55a7cdd',
        tarball: 'http://cordova.iriscouch.com/registry/_design/app/_rewrite/com
.johnwargo.carrier/-/com.johnwargo.carrier-0.0.3.tgz' },
     _from: '.',
     _npmVersion: '1.3.4',
     _npmUser: { name: 'johnwargo', email: '[email protected]' },
     directories: {} } }
name: com.johnwargo.carrier
version: 0.0.3

As you can see, publishing your plugin to the registry is a pretty straightforward process and quickly exposes your plugin to a wide audience of developers.

Wrap-Up

In this chapter, I’ve shown you how to create Cordova plugins. With the information provided in this chapter, the Cordova documentation, and the core Cordova plugins as examples to study, you have the information you need to create your own custom plugins.

I only touched upon the surface of this topic; there is much more you can do with plugins. The plugin.xml supports a lot more options than what I covered here. For example, if your plugin depends on another plugin, you can configure the plugin.xml to have Plugman or the Cordova CLI install the required plugins for you. You can also link precompiled or custom libraries into your plugin. So, if your plugin leverages some proprietary code, you can still include it but protect it from prying eyes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset