12. Working with the Cordova APIs

So far I’ve shown you a lot about Cordova: how to set up a Cordova development environment and use the tools provided by the Cordova team and the mobile platform vendors. Now it’s time, in this chapter and the two that follow, to talk more about how to build Cordova applications. In this chapter, I introduce you to the Cordova APIs and show you how to use them in your applications.

I’m not going to go super deep into each of the APIs and how they work; the Cordova documentation is a lot better than it used to be, but I will show you how to use them in your applications. In Chapter 13, “Creating Cordova Plugins,” I show you how to write your own Cordova plugins and wrap it all together in a complete Cordova application in Chapter 14, “Building a Cordova Application.”

The Cordova Core APIs

Hybrid applications become much more powerful when they can do things that simply cannot be done within the mobile browser directly. As mentioned in the first chapter, one of the purposes behind Cordova was to provide mobile web applications running within the Cordova container access to native APIs. Cordova provides access to only a subset of the APIs available on a mobile device; the APIs are

Image Accelerometer

Image Camera

Image Capture

Image Compass

Image Connection

Image Contacts

Image Device

Image Events

Image File

Image Geolocation

Image Globalization

Image InAppBrowser

Image Media

Image Notification

Image Splashscreen

Image Storage

In some cases, an API mimics a capability that is already provided in modern mobile browsers. In most cases, applications simply use the API exposed through the native browser, WebView, embedded in the Cordova container. For devices for which their browser does not expose the API, the Cordova team will implement the API so it is available across all supported mobile device platforms. You can see examples of this in the Cordova Geolocation, File and Storage APIs; they’re pretty much standard on most mobile devices, so there’s little the Cordova team has to do with them. As an example, take a look at the documentation for the Geolocation API:

This API is based on the W3C Geolocation API Specification. Some devices (Android, BlackBerry, bada, Windows Phone 7 and webOS, to be specific) already provide an implementation of this spec. For those devices, the built-in support is used instead of replacing it with Cordova’s implementation. For devices that don’t have geolocation support, the Cordova implementation adheres to the W3C specification.

Because the File and Storage APIs are standards that are already available in the mobile browser, I don’t cover them in this chapter. The Geolocation API falls into the same category, but the “Hardware APIs” section of the chapter covers other APIs that work just like it, so Geolocation gets some coverage there as well. The Contacts API is also based on a standard, but I’ll give it some coverage here as it’s unique in how it works compared to other Cordova APIs.

As mentioned in previous chapters, instead of APIs being readily available to any Cordova application, beginning with Cordova 3.0, all of the core APIs have been removed from the Cordova container and implemented as plugins. So, before your Cordova application can use any of the Cordova APIs, you must add the associated plugin to your project using a command similar to the following:

cordova plugin add path_to_plugin

So, for example, to use the Camera API in your Cordova application, you must add the camera capabilities using the following command:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git

This will pull the camera plugin files from the public Git repository; if you want to use the camera plugin from a local repository, you can pass in instead the local file path pointing to where the plugin files are located, as shown below:

cordova plugin add d:cordovacordova-3.0.0cordova-plugin-camera

You can find more information about how to use the CLI to manage plugins in Chapter 4, “Using the Cordova Command-Line Interface.”

Working with the API Cordova Documentation

As I mentioned earlier, the Cordova API documentation is pretty good. Just as there is a team working full time on the Cordova container, APIs, and plugins, there is also a dedicated team working very hard on the docs. As soon as I finish this manuscript, I plan on joining them as well. Because of all of this effort, the docs are pretty good, and you will need to spend a fair amount of time with them as you build your Cordova applications; I’ll explain why in a little bit.

To access the Cordova API documentation, point your browser of choice to www.cordova.io and look for the documentation link at the bottom of the page. When I select that link, I am redirected to http://cordova.apache.org/docs/en/3.0.0, which points to the appropriate language (English) version plus points me to the latest and greatest released version of the framework.

From the documentation page, use the drop-down shown in the upper right corner of Figure 12.1 to switch Cordova versions. The drop-down provides access to the documentation for the current Cordova version, previous versions, and even one future release of the framework.

Image

Figure 12.1 Cordova Documentation: API Quirks

When working with the APIs, you will want to pay special attention to the quirks section of the documentation for each exposed API; an example is shown on the Notification API page in Figure 12.1. Many of the APIs display different behavior on specific hardware platforms. As hard as the Cordova development team works to ensure consistency across all platforms, on some platforms an API needed to implement some feature simply isn’t available or doesn’t work as needed for the Cordova API. If you’re trying something with a Cordova API and it is not working as you expect it to, go check out the quirks for the particular API you’re working with.

Setting Application Permissions

Because most of the Cordova APIs leverage native APIs and many of them have security restrictions placed on them, in order to protect the user and/or device, developers using some Cordova APIs must set special application configuration settings for their applications before deploying them to devices. These settings configure the native application container so that it informs the mobile OS during installation that it uses specific restricted APIs. This causes the mobile OS for some platforms to prompt the user of the application during installation to allow or disallow the restricted API or APIs for the application.

As an example of how this will appear for users, take a look at Figure 12.2, which shows the security prompt displayed for users when installing the default HelloCordova application (described in Chapter 5, “Anatomy of a Cordova Application”) on an Android device.

Image

Figure 12.2 Android Application Permissions Settings Dialog


Warning

On many mobile device platforms, if a particular API or feature isn’t enabled for a Cordova application as shown in this section, or if the user disables the feature through the dialog shown in Figure 12.1, the portion of the application that uses the feature will simply fail silently when the application runs.

You would think that the application would throw some sort of error when your application tries to use a disabled or restricted API, but that’s not the case—the application simply skips the execution of the API and you’re left scratching your head as to why the feature isn’t working. If you’ve implemented a feature and it’s simply not working and not telling you why, be sure to check your application permissions. In Chapter 14, I’ll show you a trick you can use to catch those errors.


To find out what configuration settings must be set for each of the Cordova APIs your application uses, you must refer to the specific APIs documentation. Figure 12.3 shows a portion of the documentation page for the Camera API. The page contains a section called “Accessing the Feature,” which describes how to add the API plugin to the project and what configuration settings must be set for the API to work in your applications.

Image

Figure 12.3 Cordova API Documentation: Configuration Settings

As you can see from the figure, for each supported mobile platform, it describes the configuration file that must be updated and shows the content that must be included in the file to enable this feature. It’s usually a config.xml file, but the location for the file will vary across platforms.

The good news is that the Cordova CLI manages setting application permissions for you. If you add the camera API to a Cordova application using the CLI commands highlighted earlier in the chapter, the CLI will make the appropriate configuration changes for you across all of the mobile OS targets you have added to the application project. So, when you add the camera API to an application project, the appropriate settings will make it into the Android config.xml file, as shown in Listing 12.1.

Listing 12.1 Android Project config.xml File


<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.helloCordova" version="2.0.0" xmlns="http://www.w3.org/ns/widgets">
  <name>Hello Cordova</name>
  <description>A sample Apache Cordova application that
    responds to the deviceready event.</description>
  <author email="[email protected]"
    href="http://cordova.io">Apache Cordova Team</author>
  <content src="index.html" />
  <feature name="App">
    <param name="android-package"
      value="org.apache.cordova.App" />
  </feature>
  <feature name="Camera">
    <param name="android-package"
      value="org.apache.cordova.camera.CameraLauncher" />
  </feature>
  <access origin="*" />
  <preference name="useBrowserHistory" value="true" />
  <preference name="exit-on-suspend" value="false" />
  <preference name="fullscreen" value="true" />
  <preference name="webviewbounce" value="true" />
</widget>


Okay, now that I’ve covered all of the background information, let’s start talking about the APIs

Cordova Objects

Some of the Cordova APIs are not necessarily APIs; instead they’re useful objects that are exposed to a Cordova application through the plugin. In this section, I describe the connection and device objects and show you how to use them in your Cordova applications.

Connection Type

There are times when a mobile application needs to know what type of network connection it has available to it. With many mobile carriers capping data usage, the application should be careful when doing large updates to make sure it is doing its update, for example, on a lower cost (free) Wi-Fi connection rather than a cellular connection. As a best practice, mobile developers should categorize an application’s data usage patterns and write their code in order to minimize the impact on the device and the device user’s data plan costs.

To accommodate this, the Cordova framework exposes a connection object, which can be used to determine the network connection type currently in effect. To leverage the information exposed by the connection object, you must first add the network information plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-network-information.git

With that in place, a Cordova application can determine the connection type by executing the following JavaScript code:

var ct = navigator.connection.type;

Next, you can compare the value of ct against the possible connection types exposed through the following properties:

Image Connection.CELL

Image Connection.CELL_2G

Image Connection.CELL_3G

Image Connection.CELL_4G

Image Connection.ETHERNET

Image Connection.NONE

Image Connection.UNKNOWN

Image Connection.WIFI

So, if you want to make sure the application has a network connection before you do something networky, your application can do something like this:

ct = navigator.connection.type;
if (ct != Connection.NONE) {
  console.log("You have a connection");
  //Do whatever you want to do with the connection

} else {
  //Warn the user that they can't do that without a
  //network connection
  alert("No connection available!");
}


Warning

Your application has to query the connection object every time it wants to know what the current network status is. The ct variable shown in the previous example won’t maintain a valid status value as the network status changes. When I cover events later in the chapter, you will see how you can monitor network connectivity.


Or, if you are looking for a specific network type, your application can do something like this:

ct = navigator.connection.type;
if (ct == Connection.WIFI) {
  console.log("You have a WI-FI connection");
  //Do whatever you want to do over the connection

} else {
  //Warn the user that they can't do that without a WI-FI
  //network connection
  alert("Function requires WI-FI network connection!");
}

An application can also monitor the network status in real time using the online and offline events described later in the chapter.

Device

The Cordova framework also exposes a device object, which can be used to determine a limited amount of information about the device. The available device properties are shown in the following list:

Image device.name

Image device.cordova

Image device.platform

Image device.uuid

Image device.version

Image device.model

To leverage the information exposed by the device object, you must first add the device plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device.git

To see an example of the device object in action, take a look at the HelloWorld3 application described in Chapter 5.

Alerting the User

There are often times when a developer will want to notify application users of some activity. A web application can display some information on a page within the application or even open up an HTML popup, but in both cases, the management of the notification (and its removal from view) is the responsibility of the developer. To make this easier, the Cordova API includes some JavaScript functions a developer can use to provide a notification to users. There are two types of notifications, what I call hardware notifications and visual notifications. Each is described in the following two sections.

Hardware Notifications

Any modern smartphone provides an API that allows a developer to have an application make the device beep or vibrate; Cordova also exposes methods an application can call to make the device beep or vibrate as well.

To leverage hardware notifications in your Cordova applications, you must first add the vibration plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-vibration.git

Beep

To cause a mobile device to beep, a Cordova application should make a call to the navigator.notification.beep method. To have the device vibrate, a Cordova application should make a call to the navigator.notification.vibrate method. Each takes a parameter that controls how many times or for how long the notification lasts, as shown in the following examples.

The beep method accepts a parameter that controls how many times the device beeps when the method is called:

navigator.notification.beep(quantity);

To have a device beep three times, simply pass in a 3:

navigator.notification.beep(3);

Vibrate

The vibrate method accepts a duration parameter instead, which controls how long the vibration lasts:

navigator.notification.vibrate(duration);

Duration is expressed in milliseconds, so to make the device vibrate for half a second, you pass a value of 500 to the vibrate method:

navigator.notification.vibrate(500);

To make the device vibrate for 1 second, do the following:

navigator.notification.vibrate(1000);

Visual Notifications

Cordova exposes a number of methods a web application can call to allow the application to interact with the user. Web developers have always had access to the synchronous JavaScript alert(), confirm(), and prompt() methods, which can be used to interact with the user, but the Cordova versions of these functions are asynchronous and allow for additional control over the content in the dialog that is displayed to users.

To leverage visual notifications in your Cordova applications, you must first add the dialogs plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-dialogs.git

Alert

To display an alert dialog in a web application, a web developer can have an application execute the following JavaScript code:

alert("This is a JavaScript alert.");

When the code runs in a Cordova application, you will see something similar to Figure 12.4.

Image

Figure 12.4 JavaScript alert Results

If you instead use the Cordova alert() method by executing the following code:

navigator.notification.alert("This is a Cordova Alert.",
  myCallback, "Alert Test", "Click Me!")

you will see a dialog similar to the one shown in Figure 12.5.

Image

Figure 12.5 Cordova alert Results

Notice that I was able to set the title for the dialog (instead of the Cordova application reporting index.html, as shown in Figure 12.4) as well as the text on the button the user taps to close the alert.

I mentioned that the JavaScript alert was synchronous and the Cordova alert was asynchronous; this means that the JavaScript alert will likely display immediately, but the Cordova alert will display whenever the Cordova container gets around to rendering it. This is an oversimplification of what’s going on, but you can read more about it in an article I wrote here: www.johnwargo.com/index.php/mobile-development/phonegap-alerts.html.

Now, in that example, I didn’t tell you a lot about the format of the call to alert; in the following example, you can see the descriptive names of the parameters:

navigator.notification.alert(message, callback, [title], [buttonLabel])

Notice the callback parameter; it’s there to allow you to define the function that is executed when the user taps the button on the alert dialog. The way you use the Cordova alert is to execute the alert method, then use the callback function to continue program execution after the user taps the button. What happens is that any code you have after the call to alert is executed, perhaps finishing out the code in a JavaScript function, and the application will sit idle until the user taps the button; then the code in the callback function executes.

In the earlier example, I passed in a null value for the callback parameter. In this scenario, by not telling alert what function to call after the user taps the button, the Cordova container will render the specified alert dialog, then continue executing the JavaScript code that follows the call to alert and not wait for the user to tap the button.

The values for title and buttonLabel are optional; the value for title passed to the method will be used as the title for the dialog, and the buttonLabel value will be used as the text on the single button on the dialog.

Confirm

The Cordova confirm method is similar to alert except that it allows you to specify more than one button label, and the callback function is passed a numeric value indicating which button was tapped by the application user. Here’s the method signature:

navigator.notification.confirm(message, callback, [title],
  [buttonLabels]);

The following code snippet shows how to use the Cordova confirm method:

navigator.notification.confirm('Do you want to continue?',
  doContinue, 'Please confirm', 'Yes, No'),

function doContinue(buttonNum){
  navigator.notification.alert('You chose option #' +
    buttonNum + '?', null, 'Really?','Yes'),
};

The value passed to the callback function is a numeric value indicating which button was tapped. The callback function will receive a 1 if the first button was clicked, a 2 for the second button, and so on. Figure 12.6 shows confirm in action on iOS.

Image

Figure 12.6 Cordova confirm Dialog

Figure 12.7 shows the results of the doContinue function being executed, indicating that the No button was tapped in Figure 12.6.

Image

Figure 12.7 Showing confirm Results

Prompt

A Cordova application often needs to collect information from the application user outside of a web form; Cordova provides the prompt method to accommodate this requirement. The method works just like the other methods discussed in this section and has the following method signature:

navigator.notification.prompt(message, callback, [title],
  [buttonLabels], [defaultText]);

The parameters in brackets are optional. To use prompt in your Cordova applications, make a call to the prompt method and provide the necessary callback function to process the user’s input:

navigator.notification.prompt('Please enter your nickname',
  gotData, 'Nickname?', ['Cancel', 'OK'], 'Jimmy'),

function gotData(res) {
  navigator.notification.alert('You chose option #' +
    res.buttonIndex + ' You entered: ' + res.input1, null,
    'Results', 'OK'),
};


Note

Notice that prompt uses a different format for button labels than confirm uses. prompt expects an array of strings, as shown in the previous example, and confirm expects a single string with the button labels separated by a comma.


Figure 12.8 shows the example code in action on an Android emulator.

Image

Figure 12.8 Cordova prompt Dialog

Figure 12.9 shows the results displayed by gotData function.

Image

Figure 12.9 Cordova prompt Results

Cordova Events

The Cordova framework exposes a set of events a developer can use to react to certain things that happen on the device running the Cordova application. In some cases, the exposed events deal with hardware-related activities such as changes in the battery status or a physical button press by the user. In other cases, the exposed events deal with application status changes such as the application being paused or resumed. The purpose of these events is to expose to a web application the same device status events that are available to native applications.

The complete list of supported events is provided in Table 12.1.

Image

Table 12.1 Cordova Events

Most of the listed events are built into the Cordova container. Only battery status is implemented as a plugin. To enable an application to monitor battery events, you must first add the battery status plugin to your project by issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-battery-status.git

The devicready event was discussed at length in Chapter 5; to see examples of that event in action, refer to the HelloWorld applications highlighted in that chapter.

To monitor one of these events, you simply have to have an application register a listener for the particular event.

document.addEventListener("eventName", eventFunction);

As an example, to listen for the loss of network connectivity, you would register an offline event listener using the following code:

document.addEventListener("offline", isOffline);

function isOffline() {
  //Do whatever you want to do when the device goes offline

}

The isOffline function is called whenever the Cordova application that has a network connection detects that it has lost the network connection.


Warning

The events might not fire exactly when you expect them to on different mobile platforms. Be sure to test your application on different physical devices to make sure your application works as expected.


Hardware APIs

Cordova exposes a few APIs that allow developers to build applications that interact with common hardware components found in most modern smartphones. These APIs help make Cordova applications feel more like native applications, as they let a Cordova application interact with the outside world in some way.

There’s no specific grouping of these APIs in the Cordova documentation; bundling them together under the banner of hardware APIs is just my way of keeping things organized. The following APIs are discussed in this section:

Image Accelerometer

Image Camera

Image Capture

Image Compass

Image Geolocation

The Accelerator, Compass, and Geolocation APIs all work in essentially the same manner; your application can measure the current value for the particular metric, or you can set up a watch that allows your application to monitor the particular metric as it changes over time. The Camera and Capture APIs both allow you to capture photographs using the device camera, but they operate differently, plus the Capture API allows you to record video and audio files as well.

The World Wide Web Consortium has worked on defining specifications for some of these capabilities. The Compass API is defined at http://dev.w3.org/2009/dap/system-info/compass.html, the Geolocation API specification is defined at http://dev.w3.org/geo/api/spec-source.html, and the Device Orientation specification is defined at http://dev.w3.org/geo/api/spec-source-orientation.

What you’ll find is that some of the Cordova APIs align closely with the W3C specifications and others do not. For example, the Cordova Compass API has a getCurrentHeading method, while the W3C specification uses getCurrentOrientation. I assume that the Cordova APIs will align with the standards over time; you will need to monitor progress and update your applications accordingly.

In the sections that follow, I show you a little about how each of the APIs operates. Some of the APIs support a lot of options, so deep coverage is beyond the scope of this book; you can find complete examples of how to use each of these APIs in PhoneGap Essentials (www.phonegapessentials.com).

Accelerometer

The Cordova Accelerometer API allows an application to determine the device’s orientation in a three-dimensional space (using a three-dimensional Cartesian coordinate system). The Cordova documentation still says, “Captures device motion in the x, y, and z direction”; but that doesn’t make sense to me, as it’s not motion that the device is reporting, but orientation. If you hold the device steady in a particular orientation, the Accelerometer API will still report its orientation even though it’s not moving.

To enable an application to use the Accelerometer API, you must first add the device motion plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-motion.git

The API exposes three methods:

Image accelerometer.getCurrentAcceleration

Image accelerometer.watchAcceleration

Image accelerometer.clearWatch

The getCurrentAcceleration method allows an application to query the device’s current orientation. The watchAcceleration and clearWatch methods are used to allow an application to capture device orientation over a period of time, taking repeated measurements from the accelerometer at a specific time interval.

To measure the device’s current orientation, you use something like the following in your application:

navigator.accelerometer.getCurrentAcceleration(onSuccess,
  onFailure);

In this example, the call to getCurrentAcceleration includes the names of two functions: onSuccess is executed when the orientation has been successfully measured, and onFailure is executed when an error occurs; following are examples of some functions that can be used for this purpose:

function onSuccess(res) {
  x = res.x;
  y = res.y;
  z = res.z;
  var d = new Date(res.timestamp);
  timestamp = d.toLocaleString();
}

function onFailure() {
  alert('I have no idea why this failed, but it did.'),
}

The onSucess function is passed an object that represents the different parts of the accelerator measurement. The X, Y, and Z values represent the device’s orientation in a three-dimensional coordinate system, and the timestamp value indicates the date/time that the measurement was made. If you write those values out to an application’s screen, you will see something similar to what is shown in Figure 12.10, captured on an Android emulator.

Image

Figure 12.10 Accelerator Measurement Results

On an Android device, with the device lying flat on a tabletop, the accelerometer will return approximately the following values: X:0, Y:0, Z:10. As the device is flipped so it’s standing on its left edge, the values will adjust to approximately X:10, Y:0, Z:0. If you instead move the device so it’s standing on its bottom edge, the values will adjust to approximately X:0, Y:10, Z:0. Standing the device on its top edge will result in approximate accelerometer values of X:0, Y:-10, Z:0. An application uses these values to determine how a user is holding the device and is most useful for games and interactive applications.

Unfortunately, Cordova doesn’t tell you anything about any errors that occur when onFailure function is called and nothing is passed to it (an error code or error message), which can be used to identify the source of the error. So, it either works or it doesn’t, but as the app is talking directly to a physical device API that doesn’t have much complexity, if you try to determine the orientation and the call fails, it’s most likely because the device doesn’t have an accelerometer.

getCurrentAcceleration is useful if you want a quick check of a device’s orientation before doing something within your application. If you want to monitor a device’s orientation over time, in a game, for example, getCurrentAcceleration isn’t that useful. To use it in a game, you would have to write code that checks the orientation periodically. To help developers in this situation, the Cordova API allows a developer to periodically read the accelerometer by watching it using the accelerometer.watchAcceleration method.

To use accelerometer.watchAcceleration, an application sets up a watch using the following code:

var options = {frequency : 1000};
watchID = navigator.accelerometer.watchAcceleration(onSuccess,
  onFailure, options);

In this particular example, the code uses the same onSuccess and onFailure functions from the previous example. The options object defines the frequency of the accelerator measurement in milliseconds. So, to measure the current accelerometer values every 1 second, you would use 1000, as shown in the example. To measure every half second, use a frequency of 500.

In the code, the result of the call to watchAcceleration is assigned to a variable called watchID, which is used later to cancel the watch, as shown here:

navigator.accelerometer.clearWatch(watchID);

With all of this in place, the application will read the accelerometer every second and pass the values to the onSuccess function to process the results.

Compass

The compass API allows a developer to read the mobile device’s heading (using the device compass if available). The API works almost the same as the Accelerometer API; you can either query the heading value once or define a watch to periodically measure the heading value. The only differences between the two are in the results object, which is passed to the onSuccess function, and options that can be set when creating a watch.

To enable an application to read the heading, you must first add the device orientation plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-device-orientation.git

Like the Accelerator API, the Compass API exposes three methods:

Image compass.getCurrentHeading

Image compass.watchHeading

Image compass.clearWatch

The getCurrentHeading method allows an application to query the compass’s current orientation. The watchHeading and clearWatch methods are used to allow an application to capture compass headings over a period of time, taking repeated measurements from the compass at a specific time interval.

To measure the compass’s orientation, you use something like the following in your application:

navigator.compass.getCurrentHeading(onSuccess, onFailure);

In this example, the call to getCurrentHeading includes the names of two functions: onSuccess is executed when the heading has been successfully measured, and onFailure is executed when an error occurs; following are examples of some functions that can be used for this purpose:

function onSuccess(res) {
  magneticHeading = res.magneticHeading;
  trueHeading =res. res.trueHeading;
  headingAccuracy = res.headingAccuracy
  var d = new Date(res.timestamp);
  timestamp = d.toLocaleString();
}

function onFailure(err) {
  alert("Error: " + err.code);
}

The heading object (res in the example) returned to the onSuccess function has the properties described in Table 12.2.

Image

Table 12.2 Compass Results Values

When an error occurs, the onFailure function is passed an error code, which can be queried to determine the cause of the error. Possible values are CompassError.COMPASS_INTERNAL_ERR and CompassError.COMPASS_NOT_SUPPORTED.

To use compass.watchHeading, an application sets up a watch using the following code:

var options = {frequency : 1000};
watchID = navigator.compass.watchHeading(onSuccess, onFailure,
  options);

In this particular example, the code uses the same onSuccess and onFailure functions from the previous example. The options object defines the frequency of the accelerator measurement in milliseconds. So, to measure the current heading values every 1 second, you would use 1000, as shown in the example. To measure every half second, use a frequency of 500. You can also specify a filter value, which defines a minimum degree value change, which must occur before the watch is fired. Because compass values fluctuate pretty rapidly, you will want to set a filter to reduce the number of times heading measurement is made (and returned to your program) so your program can respond only to more dramatic changes in heading.

In the code, the result of the call to watchHeading is assigned to a variable called watchID, which is used later to cancel the watch, as shown in here:

navigator.compass.clearWatch(watchID);

With all of this in place, the application will read the compass every second and pass the values to the onSuccess function to process the results. To see a complete application using the Compass API, refer to Chapter 14.

Geolocation

The Cordova Geolocation API allows an application to determine the physical location of the device running an application. The API is based on the W3C’s Geolocation API and works almost the same as the Accelerometer and Compass APIs; you can either query the location once or define a watch to periodically calculate the location. The only differences between them are in the results object, which is passed to the onSuccess function, and options that can be set when creating a watch.

To enable an application to determine the device’s location, you must first add the Geolocation plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-geolocation.git

The Geolocation API exposes three methods:

Image compass.getCurrentPosition

Image compass.watchPosition

Image compass.clearWatch

The getCurrentPosition method allows an application to determine the device’s current location. The watchPosition and clearWatch methods are used to allow an application to periodically calculate the device’s location over a period of time, making repeated calculations at a specific time interval.

When the Geolocation API returns a location object, the object exposes coordinates and timestamp properties. The timestamp property contains the date and time that the measurement was made in number of milliseconds since midnight January 1, 1970. The coordinates property is another object that includes the properties described in Table 12.3.

Image

Table 12.3 Coordinates Properties

Refer to the Accelerator and Compass sections for details on how this API works.

Camera

The Cordova framework provides two APIs for working with the device camera. One is the Camera API, which provides developers with direct access to the native camera APIs, and the other is the Media Capture API described in the next section. The difference between the two options is that the Camera API exposes only the ability to take pictures with the camera, while the Media Capture API provides an interface to the camera that includes photos as well as videos plus the ability to record audio files. In this section, I show you how to use the Camera API to capture pictures from a Cordova application; refer to the section “Capturing Media Files” for information on the other options.

To enable an application to take photos using the Camera API, you must first add the Camera plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-camera.git

From a programming standpoint, getting a Cordova application to take a picture is pretty simple; all you have to do is have the program call the Camera API using the following code:

navigator.camera.getPicture(onCameraSuccess, onCameraError);

To show how this works, I created a simple camera application that makes that call to getPicture then displays the data returned from the camera. You can see a screenshot of the application in Figure 12.11.

Image

Figure 12.11 Camera Demo Application

As soon as you click the Take Photo button, the device’s native camera application opens, as shown in Figure 12.12. Manipulate the camera and image any way you want, then take the picture; in this example, you would click the camera aperture image on the right. What you will see here will vary depending on the mobile device platform the on which the application is running.

Image

Figure 12.12 Device Camera Application: Confirming Photo

Notice in Figure 12.12 that there isn’t a way to cancel taking the photo. Even if you decide not to capture a photo, you will have to snap one here; then you can cancel it in the next step.

Depending on the mobile device, you may be prompted to approve the photo, as shown in Figure 12.13. In this example, you can tap the checkmark to select the photo and return to the Cordova application; tap the X in the bottom right to cancel, returning the photo information to the Cordova application; or click the camera icon on the lower left to discard this photo and take another one. No, I’m not sure why the photo is rotated in the screen shot.

Image

Figure 12.13 Device Camera Application: Approving a Photo

When you accept a photo by tapping the checkmark icon, the device camera application will close and return information about the selected photo to the Cordova application, as shown in Figure 12.14. In this case, since I didn’t tell getPicture anything about how to take the picture or what to do with it, the API used its default settings and simply returned a file URI pointing to the image file. At this point, the Cordova application can access the file using the URI it received from the API and render the image on the screen, upload it to a server, or do anything else it wants.

Image

Figure 12.14 Camera Demo Application: Photo Results

If you choose to cancel the photo you’ve taken, the Camera API will return an error message, “Camera Cancelled,” to the Cordova application.

Now that’s not all there is to the Camera API—I was just showing you the API’s default behavior. You can also call getPicture and pass in an options object, which tells the API what to do and how to do it. Here’s an alternative way to call getPicture:

navigator.camera.getPicture(onCameraSuccess, onCameraError,
  cameraOptions);

The cameraOptions shown in the example is a JavaScript object, which can look like the following example from the Cordova documentation:

var options = {
  quality : 75,
  destinationType : Camera.DestinationType.DATA_URL,
  sourceType : Camera.PictureSourceType.CAMERA,
  allowEdit : true,
  encodingType: Camera.EncodingType.JPEG,
  targetWidth: 100,
  targetHeight: 100,
  popoverOptions: CameraPopoverOptions,
  saveToPhotoAlbum: false
};


Warning

Different platforms ignore some of these properties, so be sure to check the quirks section in the Cordova documentation before using them in your applications.


A developer will use some or all of these properties to control the picture capture process using the Camera API. Each of the possible options is described in Table 12.4.

Image
Image

Table 12.4 Camera Options


Note

The camera options targetHeight and targetWidth properties have always perplexed me; when I wrote PhoneGap Essentials more than a year and a half ago I wrote the following:

The targetHeight & targetWidth parameters are supposed to control the height and width of the image obtained using getPicture. In my testing though, the parameters did not affect the resulting picture. The documentation says as well that the parameters must be used in conjunction with each other and that aspect ratio is maintained. This further reinforces that these options cannot work as documented (which my testing has proven) since it doesn’t make sense that you have to set both height and width while at the same time maintaining an aspect ratio for the picture. If it truly was maintaining aspect ratio, then I’d expect that only one of the values would be able to be set.

As I wrote this section, I posted a question on the Cordova dev list, and from the responses I got back, it was clear that it wasn’t known how this should work. So, I committed to retest all of this and post the results. I’ll either update the Cordova documentation so it makes more sense or the Cordova developers will take a look at making this work more as expected based on what I learn. Stay tuned.


Since the camera is a separate, independent application and an application user could take several photos before finally getting the one he or she wants to return to the Cordova application, there could be quite a few photos left lying around. In PhoneGap Essentials, I described how a developer could go into the file system and clean up the orphan photos manually. Since then, the Cordova team has added a cleanup method, which can be used to clean up the orphaned photos. To use this method, call the method and pass in the names of success and failure callback functions, as shown in the following example:

navigator.camera.cleanup(onCameraCleanupSuccess,
  onCameraCleanupError);

Unfortunately, the method is currently supported only on iOS. When you try to perform the cleanup on an Android device, you will receive an “Invalid action” error message.

Capturing Media Files

The Capture API is like the Camera API in that you can use it to capture photographs, but you can also use it to capture video and audio files. The API was originally based on the W3C Media Capture API (www.w3.org/TR/media-capture-api), but at the time, the PhoneGap team didn’t or weren’t able to implement some of the APIs features. The W3C stopped work on that specification as the Device API Working group focused instead on the Media Capture and Streams API (http://dev.w3.org/2011/webrtc/editor/getusermedia.html), which isn’t like the Capture API at all.

To enable an application to use the Capture API, you must first add the Media Capture plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-media-capture.git

The API exposes the following methods:

Image capture.captureAudio

Image capture.captureImage

Image capture.captureVideo

Image MediaFile.getFormatData

The first three work exactly the same—I’ll show you how in a minute. The getFormatData is supposed to allow you to retrieve information about a media file, but because of limitations on mobile devices, very little or no information is available through this method.

To use the Capture API is pretty simple: you make a call to one of the three capture methods (audio, image, video) using the following method signature:

navigator.device.capture.captureAudio(onSuccess, onFailure,
  options);

Like many of the other APIs discussed in this chapter, the onSuccess and onFailure functions are called after the capture has completed successfully or when it fails.

The onSuccess function is passed a fileList object, which can be iterated through to access the path pointing to each captured file, as shown in the following example:

function onSuccess(fileList) {
  var len, I, path;
  //See how many files are listed in the array
  len = fileList.length;
  //Make sure we had a result; it should always be
  //at least greater than 0, but you never know!
  if(len > 0) {
    //Media files were captured, so let's process them...
    for( i = 0, len; i < len; i += 1) {
      //Get the path to the file
      path = fileList[i].fullPath;
     //Do something with the file here

    }
  } else {
    //This will probably never execute
    alert("Error: No files returned.");
  }
}

Once you have the path to each media file, you can upload the file to a server, play or display it within the app, and more.

When called, the onFailure function will be passed an error object, which can be queried to determine the error code, as shown in the following example:

var onError = function(error) {
  alert('Capture error: ' + error.code);
};

The possible error codes are

Image CaptureError.CAPTURE_INTERNAL_ERR

Image CaptureError.CAPTURE_APPLICATION_BUSY

Image CaptureError.CAPTURE_INVALID_ARGUMENT

Image CaptureError.CAPTURE_NO_MEDIA_FILES

Image CaptureError.CAPTURE_NOT_SUPPORTED

The optional options parameter controls how many media files are captured and, for audio captures, a duration property dictating the length of the audio capture.

var options = { limit: 3, duration: 10 };

Some platforms ignore some options, so be sure to check the quirks section of the Cordova Capture API documentation and test on an appropriate sampling of devices to make sure you understand how this API works.

Globalization

Many mobile applications target audiences who speak and read different languages. If you create a popular Cordova app, it probably won’t be long before you need to make it available in multiple languages. To make the globalization of a mobile application easier for developers, the Cordova team added a Globalization API that allows an application to query the OS for locale settings. Developers can use this API to determine the user’s preferred language, then load the content in the appropriate language, and can also use methods in the API to better understand how to display dates, times, numbers, and currency appropriately for the user’s preferred language.

To enable an application to leverage this API, you must first add the Globalization plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-globalization.git

The globalization object’s methods all work in a similar manner; like most of the Cordova APIs, they’re asynchronous, so you call the method and pass in success and failure functions, as shown in the following example:

navigator.globalization.getPreferredLanguage(onGPLSuccess, onGPLFailure);

The success function is passed an object your application can query to access the value or values that are returned from the method. For most generic methods, such as the preceding getPreferredLanguage method, the method returns a string value that can be passed as shown in the following example:

function onGPLSuccess(lang) {
  alert("Preferred language: " + lang.value);
}

In this example, when the call to getPreferredLanguage is made, the onGPLSuccess function executes and displays the dialog shown in Figure 12.15.

Image

Figure 12.15 Results of getPreferredLanguage

Finishing out the getPreferredLanguage example, the failure function is passed an error object, which can be queried to determine an error code and an error message, as shown in the following example:

function onGPLFailure(err) {
  alert("Error: " + err.code + " - " + err.message);
}

The possible error codes are

Image GlobalizationError.UNKNOWN_ERROR

Image GlobalizationError.FORMATTING_ERROR

Image GlobalizationError.PARSING_ERROR

Image GlobalizationError.PATTERN_ERROR

Table 12.5 lists all of the Globalization API methods as well as the parameters passed to the method and the results that are returned.

Image
Image

Table 12.5 Globalization Methods and Parameters

As you can see from the table, some of the methods accept a properties object, which allows a developer to control how a method operates. For example, to use the dateToString method to convert a date object into a string, a Cordova application would do something similar to the following:

var d = new Date();
navigator.globalization.dateToString(d, onSuccess, onFailure);

The onSuccess function is called after the date conversation has completed and is passed an object that can be queried to display the result, as shown in the following example.

function onSuccess(res) {
  alert("Result: " + res.value);
};

When the application runs on the on the Android platform, it displays a result similar to the one shown in Figure 12.16.

Image

Figure 12.16 Displaying Results from dateToString Using Default Options

The dateToString method supports an options parameter, which a developer can use to change the format of the output, as shown in the following example.

var d = new Date();
var options = {
  formatLength : 'short',
  selector : 'date'
};
navigator.globalization.dateToString(d, onSuccess, onFailure,
  options);

In this example, the options object is specifying formatLength and selector properties, which are used to control how long the resulting string is and whether it should include date and/or time values (in this case, I’m asking for only date). When the application runs, you will see a dialog similar to the one shown in Figure 12.17.

Image

Figure 12.17 Displaying Results from dateToString with Options (Short Date)

You can use an options object to specify long format, as shown in the following example:

var options = {
  formatLength : 'long',
  selector : 'date
}

Using long date format changes the application’s output to that shown in Figure 12.18.

Image

Figure 12.18 Displaying Results from dateToString with Options (Long Date)


Warning

In my testing, on iOS you get the same results no matter what you pass in for options.


In this example, the success function is passed an object with only a single property: value, but several methods will return an object with multiple properties. The stringToDate method, for example, returns an object with separate properties for each date component, as shown here:

{
  "month":7,
  "second":0,
  "millisecond":0,
  "day":31,
  "year":2013,
  "hour":10,
  "minute":47
}

When you use the Globalization API in your applications, refer to Table 12.5 and the corresponding Cordova documentation to understand exactly what each method accepts and what is returned.

Working with the Contacts Application

The Cordova Contacts API allows a developer to build an application that interacts with the address book or contacts application on a mobile device. The Cordova Contacts API is based on the W3C Contacts API (www.w3.org/TR/2010/WD-contacts-api-20100121). You would use this API if you wanted to build an application that reads from the contacts list and uses contact data within the application or uses data from within the application to write a new contact to the contacts list.

To enable an application to access a device’s contact list, you must first add the Contacts plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-contacts.git

The Contacts API is sometimes challenging to use because the contacts capabilities available on each mobile platform differ; so some of the contact fields you might use on an Android device will differ from what you would use on iOS. Additionally, the implementation of the Contacts is a little different than what we’ve seen with the other APIs.

The Contacts API essentially exposes two methods and a contacts object. The methods are used to create new contacts objects and search for contacts on a device, and the contacts object is the representation of a contact record on the device.

To create a new contact, an application makes a call to the API’s create method, as shown in the following example:

var newContact = navigator.contacts.create();

Unlike the other API methods we’ve discussed so far, this particular call is synchronous, so instead of having to provide success and failure callbacks, this operation happens immediately. However, this method call doesn’t actually create a contact in the device’s contacts application; instead, all it does is create a new contact object, nothing more. In this example, it’s creating a new, empty contact object called newContact; nothing is saved to the contacts application until you call the object’s save method, which I describe a little later.

You can also populate the contact object during the creation process by passing in a contact object to the create method, as shown in the following example:

var newContact = navigator.contacts.create(
  {"displayName": "John M. Wargo"});

In this example, I’m populating the contact object’s displayName property when I create the new contact object.

The contact object consists of the following properties; you can set some or all of these properties for a contact:

Image addresses: An array containing of all the contact’s different addresses

Image birthday: The contact’s birthday

Image categories: An array containing all of the user-defined categories associated with the contact

Image displayName: The display name for the contact

Image emails: An array containing all of the email addresses for the contact

Image id: A globally unique identifier for the contact

Image ims: An array containing all of the contact’s instant messaging addresses

Image name: An object representing each of the components of the contact’s name

Image nickname: The nickname for the contact

Image note: Any notes associated with the contact

Image organizations: An array containing all of the organizations associated with the contact

Image phoneNumbers: An array containing all of the phone numbers associated with the contact

Image photos: An array containing images associated with the contact

Image urls: An array containing the web pages associated with the contact

Notice that some of the properties are arrays of other properties. A contact can typically have two or more mailing addresses, home and work at a minimum. Additionally, many contacts have more than one email address, so the contact object has to be able to accommodate a dynamic number of properties.


Note

I could spend about 10 pages here describing all of the different arrays that are associated with the contact object, but that’s really beyond the scope of this chapter. Instead, let me show you how to save the contact, then I’ll show you how to search for contacts and dump the contact information to the console so you can more easily see how the contact object looks in the real world on different devices.

There are some inconsistencies in how different mobile devices store contact information, so seeing this in real-world scenarios is better anyway.


You can populate the contact object when you create the object, as shown in the earlier example, or you can create the contact object, then populate the object’s properties, as shown in the following example:

var newContact = navigator.contacts.create();
//Populate the contact object with values
var fullName = "John M. Wargo";
newContact.displayName = fullName
newContact.nickname = "John";

//Populate the Contact's Name entries
var tmpName = new ContactName();
tmpName.givenName = "John";
tmpName.familyName ="Wargo";
tmpName.formatted = fullName;
//Then add the name object to the contact object
newContact.name = tmpName;

In this example, I’ve created a new contact object, then started populating it with values. When it comes to populating the contact’s name information, the code populates a ContactName object (defined within the Contacts API), then adds it to the newContact object. The ContactName object includes the following properties:

Image familyName

Image formatted

Image givenName

Image honorificPrefix

Image honorificSuffix

Image middleName

There are many different object types and arrays of objects that can be added to a contact record; I’ve shown only a small example of what can be done. Be sure to use the Contacts API documentation for details on all of the supported options.

Once you have all of the contact object properties set, you must call the contact object’s save method to write the changes to the actual contact record:

newContact.save(onSuccess, onError);

The save method accepts the typical success and failure functions that you’ve seen with most of the other Cordova APIs. The failure function is passed an error object you can use to identify the cause of an error and respond accordingly, as shown in the following example:

function onError(err) {
  console.log("Error Saving Contact: " + err.code);
};

To manipulate an existing contact, you can use the Contacts API find method to locate the record, as shown in the following example:

navigator.contacts.find(contactFields, onSuccess, onError, options);

In this example, the contactFields object represents an array of field names, as shown in the following example:

var contactFields = ["displayName", "name", "phoneNumbers", "emails", "addresses"]

The find method defines which field values are returned in the search results; it does not define which fields are searched. Not what you expected, right? Me too!

The options object defines parameters around how the search is performed; a sample options object is shown here:

var options = {filter: "Wargo", multiple: true};

The filter property is used to provide the find method with the search string to use when searching records. The multiple property is a Boolean value that controls whether only a single (false) contact is returned or multiple (true) contacts are returned.

Let’s take a look at a complete example. The following code sample shows how to call find and pass in a list of contact fields and search options. In the onSuccess function, the code simply writes the contact details to the console.

function findContact() {
  var contactFields = ["displayName", "name", "phoneNumbers", "emails",
    "addresses"];
  var contactOptions = {
    filter : "Wargo",
    multiple : true
  };
  navigator.contacts.find(contactFields, onSuccess, onError,
    contactOptions);
}

function onSuccess(contacts) {
  for (var i = 0; i < contacts.length; i++) {
    console.log("Contact[" + i + "]: " + JSON.stringify(contacts[i]));
  }
}

Remember that I mentioned that there was a way to see how every aspect of a contact record was structured? Well, the following chuck of JSON is the result (slightly modified to hide my real identity) of the previous example code running against the contacts database on my personal Android device. You can run the application, search for a particular contact, then grab the JSON text returned from the call to find and analyze it to see how to populate these fields within your application.

{"id":"1370", "rawId":"109", "displayName":"Wargo, John M.", "name":{"middleName":"M.",
"familyName":"Wargo", "formatted":"John M. Wargo", "givenName":"John"}, "nickname":"John",
"phoneNumbers":[{"type":"mobile", "value":"(555) 555-3333", "id":"58", "pref":false},
{"type":"work", "value":"(555) 555-4444", "id":"59", "pref":false}, {"type":"home",
"value":"(555) 555-6666", "id":"12860", "pref":false}], "emails":[{"type":"custom",
"value":[email protected] ,"id":"901", "pref":false}],
"addresses":[{"region":"CA","streetAddress":"99 Cordova lane ", "id":"902", "formatted":"99
Cordova Lane San Francisco, CA 99215 United States of America", "postalCode":"99215",
"locality":"San Francisco", "type":"home", "pref":false, "country":"United States of
America"}, {"region":"CA", "streetAddress":"One Sybase Drive", "id":"12861",
"formatted":"One Sybase Drive Dublin, CA 94568 United States of America",
"postalCode":"94568", "locality":"Dublin", "type":"work", "pref":false, "country":"United
States of America"}], "ims":null, "organizations":null, "birthday":null, "note":null,
"photos":null, "categories":null, "urls":"www.johnwargo.com"}

You can see, for example, how phone numbers are managed within the contacts application in this example:

"phoneNumbers":[
  {"type":"mobile", "value":"(555) 555-3333", "id":"58", "pref":false},
  {"type":"work", "value":"(555) 555-4444", "id":"59", "pref":false},
  {"type":"home", "value":"(555) 555-6666", "id":"12860", "pref":false}
]

Each phone number has a specific type, value, ID, and preferred status value. ID should be created automatically when adding the phone number to the contact record.

Different mobile device platforms manage contact data differently, so be sure to test the contact format on each mobile device you will be supporting—you might have to deal with each platform differently.

Once you have a contact object returned from the call to find, you can change the properties of the object and write the changes back to the contacts application using the save method discussed earlier. To remove the contact, first get a handle to the contact object and make a call to remove:

foundContact.remove(onSuccess, onFailure);

There’s a lot more to the Contacts API. I’ve only touched the surface—be sure to leverage the Cordova documentation for more information.

Playing/Recording Media Files

The Cordova APIs include a Media API an application can use to record and play media files. This is the API you would use to play audio files in the background of a smartphone or tablet video game, for example.

To enable an application to work with media files, you must first add the media plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-media.git

The Media API is like most of the other Cordova APIs in that the API’s methods are asynchronous, but what triggers the callback functions is a little different. To use this API, an application starts by creating a Media object using code similar to the following:

var mediaObj = new Media(srcFile, onSuccess, onError, onStatus);

What this code does is create a mediaObj object that points to the media file specified in the srcFile parameter shown in the example. The application doesn’t open or connect to the file yet; it merely creates an object that refers to the file, nothing more. Some methods I’ll show you in a little while are used to actually play the file.

The onSuccess and onFailure functions shown in the example are the success and failure callback functions you should be familiar with by now, but they don’t fire when you might expect. Since the code I’ve shown is only creating an object, there are no real callback functions that need to be executed as part of that process. The onSuccess, onFailure, and onStatus callback functions shown in the example are actually called when any of the following methods are called against the media object just created:

Image getCurrentPosition

Image getDuration

Image pause

Image play

Image release

Image seekTo

Image setVolume

Image startRecord

Image stop

Image stopRecord

So, to play a media file called soundtrack.mp3, an application would execute the following code:

srcFile = 'soundtrack.mp3';
var mediaObj = new Media(srcFile, onSuccess, onError, onStatus);
mediaObj.play();

function onSuccess() {
  console.log("Media: Success");
}

function onError(error) {
  alert('Media Error: ' + error.code + ': ' + error.message);
}

function onStatus(statCode) {
  console.log("Media Status: " + statCode);
}

To stop a media file from playing, simply call mediaObj.stop();.

As you can see in the example, the onStatus function is passed a status code parameter that allows an application to understand what’s currently going on with media playback or recording. The possible status codes are

Image Media.MEDIA_NONE

Image Media.MEDIA_STARTING

Image Media.MEDIA_RUNNING

Image Media.MEDIA_PAUSED

Image Media.MEDIA_STOPPED

With all of the methods and callbacks available with the Media API, you can build a complete media player application, or you can simply load up an audio file and play it without any UI; the API provides the flexibility a developer needs to do either.


Warning

The location where a Cordova application stores the media files packaged with the application varies across the different mobile device platforms. Android files are located in the /android_asset folder, whereas on iOS, files are located within the root of the application’s file area.


InAppBrowser

The InAppBrowser is a more recent edition to the Cordova APIs. It allows a web application to load content into a separate window. Originally created as independent Cordova plugins called ChildBrowser for Android and iOS, it was added to the Cordova project as InAppBrowser, then expanded to support other mobile device platforms.

Say, for example, that you want to show your application users additional web content. You could easily load additional content within your application and manage transitions to and from the content within your application, but sometimes you want a different experience for your users. You can also load the content in the system browser, but on iOS, for example, the user would have to perform manual steps to navigate back to your application after looking at the content in the browser. InAppBrowser loads web content in such a way that your application users can more easily return directly to the main application.

To enable an application to use the InAppBrowser, you must first add the InAppBrowser plugin to your project by opening a terminal window and issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-inappbrowser.git

Loading Content

With the plugin added to your project, you can now open web content in a window using the following code:

var ref = window.open('http://www.johnwargo.com', '_blank',
  'location=yes'),

In this example, the application will open my personal website and return an object that represents the browser window. You can later use the returned object to interact with the browser window.

You could also create the browser window, but not display it, by using the following code:

var ref = window.open('http://www.johnwargo.com', '_blank',
  'hidden=yes'),

Later on, when you’re ready to display the browser window, you can open it using:

ref.show();

Notice that there are no callback functions that we’re used to seeing with the Cordova APIs.

Of the parameters passed to the call to window.open, the _blank tells the application to open the content in its own window; you could also use _self, which tells it to open the page within the current window, and _system, which tells it to open the content in the system’s default web browser.

The problem with a target of _self is that the page that is being loaded replaces the current web content for the application. For application users, this means that there’s no going back—there isn’t an iOS button to use for that, anyway, and on Android, the escape button won’t take you back either.

The 'location=yes' tells the InAppBrowser to display the page location within the browser window. There are several other options that can be used when loading a page, but the options vary depending on the mobile device platform; refer to the Cordova documentation for more information.

If you run the application on an Android device, you will see a screen similar to the one shown in Figure 12.19; I’ve cropped the screen to show only the top portion of the window. In this case, the browser window opens with an address bar a user can use to navigate to other sites as well. The user should tap the Done button to return to the original program.

Image

Figure 12.19 InAppBrowser Opening an External Web Page with an Address Bar on Android

When the same application is executed on an iOS device, the user will see the web page, and at the bottom, iOS will display the page address (without the ability to manipulate it or navigate to other sites) plus the Done button to close the window, as shown in Figure 12.20.

Image

Figure 12.20 InAppBrowser Opening an External Web Page with an Address Bar on iOS

Figure 12.20 highlights one of the key benefits of using InAppBrowser on an iOS device. When done reading the content, all the user has to do here is tap the Done button to return to the main application. If the page were loaded by spawning the system browser to open the page, the user would have to double-tap the device’s Home button to bring up a list of running applications, then tap on the application name to return to the application—not the best user experience.

To turn off display of the page address, open a page with the following code:

var ref = window.open('http://www.johnwargo.com', '_blank',
  'location=no'),

On Android, the page will load like in the previous examples but will not display the address bar, as shown in Figure 12.21.

Image

Figure 12.21 InAppBrowser Opening an External Web Page without an Address Bar on Android

On iOS, the Done button remains, but the page address is removed, as shown in Figure 12.22.

Image

Figure 12.22 InAppBrowser Opening an External Web Page without an Address Bar on iOS

You can also use InAppBrowser to load local content, as shown in the following example:

var ref = window.open('help.html', '_blank'),

In this case, I’ve added an HTML file called help.html to the Cordova project’s www folder. The Cordova prepare command makes sure the file is copied over to the right location for each supported mobile platform project and is available to be loaded by the application as needed. You can see an example of the page loading on an Android device in Figure 12.23.

Image

Figure 12.23 InAppBrowser Opening a Local Web Page

Notice that the location bar is loaded by default, since I didn’t specify one in the call to window.open, and that the file’s location on Android is in the android_asset/www folder.

To close an InAppBrowser window, simply call the close method on the window object:

ref.close();

As fun an interesting as it is to load web content into another window, there are also ways that an application can interact with the window. In the next few sections, I highlight the different ways for interacting with the browser window from within a Cordova application.

Browser Window Events

There are many scenarios in which an application will want to know what is going on within an InAppBrowser window. To accommodate those requirements, the InAppBrowser API fires different events at different times in the InAppBrowser window lifecycle. The supported events are

Image loadstart: Fires when the InAppBrowser begins to load a URL

Image loadstop: Fires when the InAppBrowser completes loading a URL

Image loaderror: Fires when the InAppBrowser encounters an error while loading a URL

Image exit: Fires when the InAppBrowser window is closed (either by the user or by the application calling the close method)

To flesh out my example from earlier, here’s a block of code that opens a local HTML file using InAppBrowser, then defines event listeners for each of the new window’s events:

var ref = window.open('help.html', '_blank'),
ref.addEventListener('loadstart', onEvent);
ref.addEventListener('loadstop', onEvent);
ref.addEventListener('loaderror', onLoadError);
ref.addEventListener('exit', onEvent);

Notice that instead of having a separate event callback function for each, I have only implemented two callback functions, one for errors and another for everything else. This is because when anything but an error event fires, the callback function is passed an event object that describes the event that was fired, as is illustrated in the following code:

function onEvent(event) {
  console.log('Type: ' + event.type);
  console.log('URL: ' + event.url);
  //Do something based on event.type

}

Developers can query event.type and do whatever is appropriate for the particular event that has fired and dramatically simplify the code being executed.

When an error occurs, the error callback function is passed an object that includes code and message properties, as illustrated in the following code. Developers can then query event.code and display an appropriate error message or perform the appropriate recovery steps as needed.

function onLoadError(event) {
  console.log('onLoadError: ' + event.code + ' - ' + event.message));
}

Execute Scripts

There are times when simply loading web content in a separate window isn’t enough; you might need to modify content or execute some JavaScript within the page. To accommodate this need, the InAppBrowser includes a method that allows an application to execute JavaScript code within the InAppBrowser window.

To make use of this feature in your application, you have your application execute the executeScript method, as shown in the following example:

ref.executeScript(scriptInfo, onSuccess);

The onSuccess function passed to the method is the standard success callback function you’ve seen used throughout this chapter. The scriptInfo parameter in the example defines what JavaScript code is executed and where the code is obtained from: either passed directly to the method or loaded from a file.

To execute a specific piece of JavaScript code, you would pass in a JavaScript object with a property of code and a value that consists of a string containing the JavaScript code being executed:

{code : "$('#heading').replaceWith('<h2>This is some injected text</h2>'),"}

In this example, the code is using the jQuery replaceWith function to replace some of the content within the loaded web page.

You can’t execute your JavaScript code until the page has finished loading, so you will most likely add the call to executeScript to some part of your code that you know will execute after the page has completed loading, such as the loadstop event listener shown in the following example:

var ref = window.open('help.html', '_blank', 'location=no'),
ref.addEventListener('loadstop', function() {
  ref.executeScript({
    code : "$('#heading').replaceWith('<h2>This is some injected text</h2>'),"
  }, onSuccess);
});

To illustrate this example, I added a div called heading to the top of the local help.html file used in a previous example. Then, when the example code provided executes, after the page loads, you will see the div’s content update, as shown in Figure 12.24.

Image

Figure 12.24 InAppBrowser Showing the Results of executeScript

Instead of passing in your JavaScript code directly to the executeScript method, you can save your code to a file and pass the file name to executeScript via the scriptInfo parameter, as shown here:

{file : "myscript.js"}

The end result is the same; only the source of the JavaScript code changes in the example code shown earlier.

Insert CSS

Along with the ability to execute script within an InAppBrowser window, you can also use a method exposed by the InAppBrowser to insert CSS into its window. Say, for example, the page you’ve loaded into the InAppBrowser window came from an external source, and you want to change the styling of the page to match the rest of your application, y ou can easily change the CSS for the page on the fly. To do this, code the application to call the InAppBrowser’s insertCSS method and pass in either the CSS or a reference to a CSS file you want inserted, as shown in the following example:

ref.insertCSS(cssInfo, onSuccess);

The onSuccess function passed to the method is the standard success callback function you’ve seen used throughout this chapter. The cssInfo parameter in the example defines what CSS is inserted and where the CSS is obtained from: either passed directly to the method or loaded from a file.

To pass in a specific piece of CSS, you would pass in a JavaScript object with a property of code and a value that consists of a string containing the CSS being inserted:

{code : "body {background-color:black; color:white}"}

You can’t insert your CSS until the page has finished loading, so you will most likely add the call to insertCSS to some part of your code that you know will execute after the page has completed loading, such as the loadstop event listener shown in the following example:

var ref = window.open('help.html', '_blank', 'location=no'),
ref.addEventListener('loadstop', function() {
  ref.insertCSS({
    code : "body {background-color:black; color:white}"
  }, onSuccess);
});

Figure 12.25 shows the page loaded with the modified CSS; notice how I switched page colors, making the background black and the text color white.

Image

Figure 12.25 InAppBrowser Showing the Results of insertCSS

Instead of passing in your CSS directly to the insertCSS method, you can save it to a file and pass the file name to insertCSS via the cssInfo parameter, as shown here:

{file : "mystuff.css"}

The end result is the same; only the source of the CSS changes in the example code shown earlier.

Splash Screen

Cordova provides a Splashscreen API an application can use to display a custom splash screen when a Cordova application launches. To enable an application to use the Splashscreen API, you must first add the Splashscreen plugin to your project by issuing the following CLI command from the Cordova project folder:

cordova plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-splashscreen.git

You need to do a bit of work to create the appropriate splash screen graphic files configured and scaled to support the variety of mobile device platforms as well as the multiple form factors per mobile OS. There’s also some manual setup you have to do for each mobile device platform: modifying the Android Cordova container source code. For those reasons, this particular topic is a little more complicated than appropriate for the scope of this chapter.

From a coding standpoint, once you have the appropriate graphics created and added to your Cordova and platform projects, you can display and hide your application’s splash screen using the following code:

function showSplash() {
  navigator.splashscreen.show();
  setTimeout(hideSplash, 2000);
}

function hideSplash() {
  navigator.splashscreen.hide();
}

In this example, the showSplash function displays the splash screen, then sets up a timer to have the splash screen hide itself after 2 seconds.

Wrap-Up

A lot went on in this chapter. I introduced most of the Cordova APIs and showed you how to leverage them in your Cordova applications. I didn’t go into a lot of detail for each but tried to show you enough for you to understand how the APIs worked and get you started with the way they’re invoked and what the responses look like. You will need to spend some time digging into the Cordova documentation for additional information about all of the options supported by each API.

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

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