13. 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 while—but with the release of Cordova 3.0 and the capabilities provided by plugman and the Cordova CLI, plugins have changed a bit. In this chapter, I show you how to create a JavaScript-only plugin as well as a native plugin for both Android and iOS. The process is essentially the same for other mobile platforms, so you need to refer to the documentation for coverage of other platforms, such as BlackBerry 10 and Windows Phone 8.

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 introduction documents that describe how to create plugins. The “Plugin Development Guide” (http://tinyurl.com/ka68thu) 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://tinyurl.com/kwz42ln and iOS at http://tinyurl.com/kcmnp2g.

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 Interface”) 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, 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 which parts get copied where and which files are unique to each mobile platform. There are even settings in the plugin.xml, which 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 for your plugin.

Beyond those first two requirements, the rest of the plugin files are anything else you need to define your plugin. In general, a plugin includes one or more native source code files for each supported mobile device platforms. 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. Figure 13.1 shows the contents of the Cordova 3.0 download package. The .zip files that have “plugin” in their name were created by the Cordova team; you can extract the files and analyze them for hints on how to build your own plugins. One of the easiest ways to get started is to take an existing plugin and modify it to suit your particular needs.

Image

Figure 13.1 Cordova 3.0 Installation Files

Creating a Simple 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 for Android and/or iOS. In this section, I show you how to create a plugin composed of only JavaScript code. In the sections that follow, I expand on what we learn here and build a more complicated example plugin.


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 create a plugin that calculates the meaning of life. For those of you who are aware of Douglas Adams’s work, this will make perfect sense to you; 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 here: http://en.wikipedia.org/wiki/42_(number).


The plugin I’m creating is called Meaning of Life, which I will abbreviate to MoL. To create this plugin, I created a mol folder in my system’s dev folder. Next, I created a file called plugin.xml and placed it in the new folder. The plugin.xml file is a simple XML file that describes the plugin for plugman and the Cordova CLI; you can see the complete listing of the file I used in Listing 13.1; I simply copied it from another plugin and modified it to suit my plugin’s needs.

Listing 13.1: plugin.xml


<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="com.cordovaprogramming.meaningoflife" version="1.0.0">
  <name>MoL</name>
  <description>Calculates the Meaning of Life (an obscure
    reference to Douglas Adams's Hitchhiker's Guide to the
    Galaxy)
  </description>
  <author>John M. Wargo</author>
  <keywords>mol, Meaning of Life</keywords>
  <license>Apache 2.0 License</license>
  <engines>
    <engine name="cordova" version="3.0.0" />
  </engines>
  <js-module src="mol.js" name="mol">
    <clobbers target="mol" />
  </js-module>
  <info>This plugin exists for no other reason but to
    demonstrate how to build a simple, JavaScript-only
    plugin for Apache Cordova.</info>
</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 13.1.

Image

Table 13.1 Cordova plugin.xml Elements

Next, I created a JavaScript file called mol.js and placed it in the mol folder. The complete file listing is provided in Listing 13.2, and it is only 7 lines of code. The code simply creates a mol object, then defines within it the calculateMOL function used to calculate the Meaning of Life. The last line in the file exports the mol object, which is what makes the object and its associated function available to Cordova applications that use the plugin.

Listing 13.2: mol.js


var mol = {
  calculateMOL : function() {
    return 42;
  }
};

module.exports = mol;


That’s it—that’s all there is to creating a simple Cordova plugin. To prove that it works, I created the simple MoL Demo application shown in Listing 13.3. First, I opened a terminal window, then navigated to my system’s dev folder, then issued the following CLI commands:

cordova create simplePlugin com.cordovaprogramming.simplePlugin SimplePlugin
cd simplePlugin
cordova platform add android ios
cordova plugin add c:devpluginsmol

With those steps completed, all I had to do was open my HTML editor of choice and enter the code in Listing 13.3. In the application, I created a simple button, and when the button is tapped, my doMOL function is executed, which makes the call to mol.calculateMOL() and displays the result in an alert dialog.

Listing 13.3: MoL Demo Application (simpleplugin.html)


<!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;" />
    <script type="text/javascript" charset="utf-8"
      src="cordova.js"></script>
    <script type="text/javascript" charset="utf-8">

      function onBodyLoad() {
        document.addEventListener("deviceready", onDeviceReady,
          false);
      };

      function onDeviceReady() {
        //alert("onDeviceReady");
      };

      function doMOL() {
        var res = mol.calculateMOL();
        alert('Meaning of Life = ' + res);
      }
    </script>
  </head>
  <body onload="onBodyLoad()">
    <h1>MoL Demo</h1>
    <p>
      This is a Cordova application that uses my custom
      Meaning of Life plugin.
    </p>
    <button onclick="doMOL();">
      Calculate Meaning of Life
    </button>
  </body>
</html>


That’s all there is to it. When you run the application and tap the button, you should see the results shown in Figure 13.2.

Image

Figure 13.2 Calculating the Meaning of Life

Creating a Native Plugin

Now that I’ve shown you how to create a simple, JavaScript-only plugin, it’s time to go beyond that simple example and create a plugin for Cordova that contains native code. In the next two sections, I show you how to create a simple native plugin for Android and how to add the same functionality for iOS.

This example plugin 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 simple and 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 simple 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 APIs to your plugins will be easy once you know how plugins work.

The plugin 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.

When I created the plugin, I created a folder for the plugin, as shown in Figure 13.3. Within that folder are the JavaScript interface definition plus the plugin.xml. I created a subfolder called src and added android and ios folders there to store that native code for each of the platforms. This isn’t a required structure for your plugins but makes it easy to keep things organized.

Image

Figure 13.3 Plugin Folder Structure

Before I start on the native code, I have 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 methods will not do any calculations and return any values directly; instead, they will make calls to a cordova.exec method, which 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 calls callback functions and passes its results back to the JavaScript object.

The method signature for cordova.exe looks like the following:

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 that is executed. Lastly, the arguments parameter is an optional array of arguments that will be passed to the pluginMethod.

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 13.4 shows the contents of carrier.js, the JavaScript interface file for the plugin. It begins with the declaration of a cordova object, which refers to the loading of the cordova JavaScript library that exposes the exec function. With that in place, the carrier object is created, and the two methods are defined. Each calls cordova.exec and passes the necessary function names and object and method names needed for the cordova object to locate the correct native object and methods to do the work I need done.

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

Listing 13.4: carrier.js


var cordova = require('cordova'),

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

  getCountryCode : function(successCallback, errorCallback) {
    cordova.exec(successCallback, errorCallback,
    'CarrierPlugin', 'getCountryCode', []);
  }
};

module.exports = carrier;


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.

You can create Android plugins by creating a new Cordova project, then wiring the Java classes and JavaScript interface file into the application. Then, when the plugin is working in the application, you can pull it out into a separate folder for distribution as a plugin. For this work, I simply created a folder for the plugin and did all my work there, then created a new Cordova project and used the Cordova CLI to add my new plugin there. This allowed me to work on my plugin files separately and test the plugin.xml file at the same time. You may find that this approach doesn’t work for you for your plugin development efforts. It probably just depends on how complicated the plugin code is; in this case, the code was simple enough that I didn’t have to deal with a lot of compilation errors, so working on the plugin as a standalone project was easier for me.

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, within the application, define an instance of an object 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 13.5 lists the Java code used for the Android plugin; it defines a simple class called CarrierPlugin that exposes the exec method, which is what is executed by the call to the JavaScript cordova.exec highlighted in Listing 13.4.

The class defines two constants, ACTION_GET_CARRIER_NAME and ACTION_GET_COUNTRY_CODE, which 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. Next, the code 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, then 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, then 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.

Listing 13.5: CarrierPlugin.java


package com.cordovaprogramming.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 CarrierPlugin 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;
    }
  }
}


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 create the plugin’s plugin.xml file, shown in Listing 13.6. The file is similar to the one I used for the MoL plugin, but as there are native components of this one, there are some extra settings to explain. First take a look at the file; I’ll describe the contents after the listing.

Listing 13.6: plugin.xml


<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="com.cordovaprogramming.carrier"
version="1.0.0">
  <name>Carrier</name>
  <author>John M. Wargo</author>
  <description>Exposes mobile carrier related values to a Cordova application.</description>
  <keywords>carrier</keywords>
  <license>Apache 2.0 License</license>
  <engines>
    <engine name="cordova" version="3.0.0" />
  </engines>
  <js-module src="carrier.js" name="carrier">
    <clobbers target="carrier" />
  </js-module>
  <platform name="android">
    <!-- android-specific elements -->
    <source-file src="src/android/CarrierPlugin.java" target-dir="src/com/cordovaprogramming/carrier" />
    <config-file target="res/xml/config.xml" parent="/*">
      <feature name="CarrierPlugin" >
        <param name="android-package" value="com.cordovaprogramming.carrier.CarrierPlugin"/>
      </feature>
    </config-file>
    <config-file target="AndroidManifest.xml" parent="/*">
      <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    </config-file>
  </platform>
  <platform name="ios">
    <!-- ios-specific elements -->
  </platform>
</plugin>


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

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

The Cordova application accesses the getCarrierName method through the carrier object:

carrier.getCarrierName(onSuccess, onFailure);

The only part of the file that is different from the earlier example (Listing 13.1) is the platform section:

<platform name="android">
  <!-- android-specific elements -->
  <source-file src="src/android/CarrierPlugin.java"
    target-dir="src/com/cordovaprogramming/carrier" />
  <config-file target="res/xml/config.xml" parent="/*">
    <feature name="CarrierPlugin" >
      <param name="android-package"
        value="com.cordovaprogramming.carrier.CarrierPlugin"/>
    </feature>
  </config-file>
  <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 platform and contains settings related 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 following one 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 code files, which need to be installed by the CLI when the plugin is installed. The following example instructs plugman or the CLI to copy the file called CarrierPlugin.java located in the plugin source folder’s src/android folder to the Cordova project’s Android platform folder in the src/com/cordovaprogramming/carrier folder:

<source-file src="src/android/CarrierPlugin.java"
  target-dir="src/com/cordovaprogramming/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 such a folder structure. 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 specifies that a feature named CarrierPlugin should be added to the Android project’s config.xml file and should point to the Java Class com.cordovaprogramming.carrier.CarrierPlugin:

<config-file target="res/xml/config.xml" parent="/*">
  <feature name="CarrierPlugin" >
    <param name="android-package"
      value="com.cordovaprogramming.carrier.CarrierPlugin"/>
  </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, which 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:

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

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

Image

Figure 13.4 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 your permissions.

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

cordova create nativePlugin com.cordovaprogramming.nativePlugin NativePlugin
cd nativePlugin
cordova platform add android
cordova plugin add c:devpluginscarrier

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

Listing 13.7: 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;" />
    <script type="text/javascript" charset="utf-8"
      src="cordova.js"></script>
    <script type="text/javascript" charset="utf-8">
      function onBodyLoad() {
        document.addEventListener("deviceready", onDeviceReady,
          false);
      };

      function onDeviceReady() {
        //Nothing to do here really
        //but I always include this just in case
      };

      function doSomething1() {
        carrier.getCarrierName(onSuccess, onFailure);
      }

      function doSomething2() {
        carrier.getCountryCode(onSuccess, onFailure);
      }

      function onSuccess(result) {
        var resStr = "Result: " + result;
        console.log(resStr);
        alert(resStr)
      }

      function onFailure(err) {
        console.log("onFailure: " + JSON.stringify(err));
        alert("Failure: " + err);
      }
    </script>
  </head>
  <body onload="onBodyLoad()">
    <h1>Carrier Demo</h1>
    <p>
      This is a Cordova application that uses my fancy new Carrier plugin.
    </p>
    <button onclick="doSomething1();">
      Get Carrier Name
    </button>
    <button onclick="doSomething2();">
      Get Country Code
    </button>
  </body>
</html>


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

Image

Figure 13.5 Carrier Demo Application

When you tap the Get Carrier Name button, the application calls the appropriate Telephony API and returns the carrier name, as shown in Figure 13.6. I took that particular screenshot on an Android emulator, so it returns Android rather than a particular carrier’s name. I have an Android tablet, but it’s provisioned for data only, so the API doesn’t return anything.

Image

Figure 13.6 Results of getCarrierName on an Android Emulator

When you tap the Get Country Code button, the application calls the appropriate Telephony API and returns the country code, as shown in Figure 13.7.

Image

Figure 13.7 Results of getCountryCode on an Android Emulator

That’s it—I now have a native plugin for Android, and it’s all ready to go.

Creating the iOS Plugin

In this section of the chapter, I show you how to implement the iOS version of the native plugin discussed in the previous section.

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. To get started, I navigated to my plugin’s src/ios folder and created two files: CarrierPlugin.h and CarrierPlugin.m. The .h file is the iOS plugin’s Header file and defines the interface for the iOS plugin code. The .m file is the implementation of the interface and contains the code that does the work for the plugin.

Listing 13.8 shows the content of the CarrierPlugin.h file. Here, I define the interface for CarrierPlugin 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 that are available: getCarrierName and getCountryCode. Sound familiar? They should—those are the methods that will be called by the plugin’s JavaScript interface shown in Listing 13.1.

Listing 13.8: CarrierPlugin.h


#import <Cordova/CDVPlugin.h>

@interface CarrierPlugin : CDVPlugin {

}

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

@end


Listing 13.9 shows the contents of the CarrierPlugin.m file; this is the implementation of the interface described in Listing 13.8. Here the code imports some of the interfaces used by the plugin, then defines the functions that are called when the Cordova application invokes getCarrierName and getCountryCode.

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, which is assigned to an instance of the CTTelephonyNetworkInfo class, as shown here:

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

Next, a carrier object is created; it provides the plugin with access to the cellular provider properties:

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. Listing 13.9 shows the complete code listing for the file.

Listing 13.9: CarrierPlugin.m


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

@implementation CarrierPlugin

- (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


With the plugin’s code in place, the last thing that has to happen is that the plugin.xml file must be updated 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">
  <!-- ios-specific elements -->
  <header-file src="src/ios/CarrierPlugin.h" />
  <source-file src="src/ios/CarrierPlugin.m" />
  <config-file target="config.xml" parent="/*">
    <feature name="CarrierPlugin" >
      <param name="ios-package" value="CarrierPlugin"/>
    </feature>
  </config-file>
</platform>

For iOS plugins, the platform element adds a special element called header-file, which defines the names of any header files (.h files) used by the plugin. In the plugin.xml file segment shown, I have added a header-file element for the plugin’s only header file (CarrierPlugin.h) and a source-file element for the implementation file (CarrierPlugin.m). They both get copied to the same location within a Cordova project folder structure.

There is one config.xml file update that has to be made: we have to add the CarrierPlugin feature to the file and point to the CarrierPlugin interface shown in Listing 13.8.

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 I need defined for the plugin. Listing 13.10 shows the complete listing for the plugin.xml file.

Listing 13.10: Completed plugin.xml File Contents


<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="com.cordovaprogramming.carrier"
version="1.0.0">
  <name>Carrier</name>
  <author>John M. Wargo</author>
  <description>Exposes mobile carrier related values to a
    Cordova application.</description>
  <keywords>carrier</keywords>
  <license>Apache 2.0 License</license>
  <engines>
    <engine name="cordova" version="3.0.0" />
  </engines>
  <js-module src="carrier.js" name="carrier">
    <clobbers target="carrier" />
  </js-module>
  <platform name="android">
    <!-- android-specific elements -->
    <source-file src="src/android/CarrierPlugin.java"
      target-dir="src/com/cordovaprogramming/carrier" />
    <config-file target="res/xml/config.xml" parent="/*">
      <feature name="CarrierPlugin" >
        <param name="android-package"
          value="com.cordovaprogramming.carrier.CarrierPlugin"/>
      </feature>
    </config-file>
    <config-file target="AndroidManifest.xml" parent="/*">
      <uses-permission
        android:name="android.permission.READ_PHONE_STATE" />
    </config-file>
  </platform>
  <platform name="ios">
    <!-- ios-specific elements -->
    <header-file src="src/ios/CarrierPlugin.h" />
    <source-file src="src/ios/CarrierPlugin.m" />
    <config-file target="config.xml" parent="/*">
      <feature name="CarrierPlugin" >
        <param name="ios-package" value="CarrierPlugin"/>
      </feature>
    </config-file>
  </platform>
</plugin>


To test the plugin, I created a new project and added my plugin 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 13.8.

Image

Figure 13.8 Xcode Build Error

This happened because the project didn’t have the iOS CoreTelephony framework added to the project. To fix this error, I clicked on the project name in the Xcode project navigator, opened the Build Phases tab, and added the framework to the Libraries section, as shown already completed in Figure 13.9.

Image

Figure 13.9 Xcode Project Build Phases Settings

I then clicked the plus sign highlighted in the bottom of Figure 13.9, and in the dialog that appeared, I started to type telephony, as shown in Figure 13.10. 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 13.10 Xcode Add Framework Dialog

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

Image

Figure 13.11 Results of getCarrierName on an iOS Device

I couldn’t test this on an iOS simulator because the simulator is not provisioned with a mobile carrier and therefore cannot determine any carrier-specific settings, so it returns null. When I run the application on the simulator and tap the Get Carrier Name button, I receive the results shown in Figure 13.12.

Image

Figure 13.12 Results of getCarrierName on an iOS Simulator

Now, I could code 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.

Deploying 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.

You can also distribute your plugins through GitHub (www.github.com). You can create your own GitHub account and post your plugin files there. Then, once the files are up there, you can start socializing your plugin so other developers can learn about it. The PhoneGap team has created a repository located at https://github.com/phonegap/phonegap-plugins where you can publish information about your plugins. The team is also working on a plugin discovery system that would allow developers to more easily locate plugins to use in their applications.

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 skimmed the surface of this topic; there is a lot more you can do with plugins. The plugin.xml supports many 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 in 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