10. Accelerometer

The Accelerometer API allows a PhoneGap application to determine a device’s orientation in a three-dimensional space (using X, Y, and Z coordinates). The current PhoneGap API documentation claims that the values returned by the accelerometer indicate the changes in a device’s motion through space, but in testing what the accelerometer returns are values that define the device’s actual orientation in a three-dimensional space. If the accelerometer were actually measuring motion through space, then the accelerometer API would return no information when the device is stationary, which is not the case.

For example, 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.

PhoneGap developers can query an API to determine a device’s orientation at a particular time or can watch the accelerometer to monitor the device’s acceleration repeatedly over a periodic time interval. Determining motion through space is simply a matter of comparing subsequent orientation measurements and calculating the difference between them.

The API returns accelerometer values that vary depending on the device OS. For example, devices running BlackBerry Device Software 7 return values from about –1000 to 1000, while Android devices as shown return values from about –10 to 10. Your applications will need to provide for this variance and adjust their accelerometer scaling depending on which mobile platform the application is running on. This is yet another reason why testing on physical devices and on all supported platforms is important for any developer.

Holding the device in position will result in some variance in values; the device is “moving” the tiniest bit after all, so your applications will have to adjust for minor movement of the device and respond to what are clearly true movements of the device through space. Most likely your application will simply ignore the last few decimal places in the measurement or convert to the nearest integer.

In most cases, you will need to test applications that use the Accelerometer API on physical devices. The iPhone simulator and the Android emulators, for example, don’t include an option for setting device orientation except in the iPhone’s case where you can simulate shaking the device. Only a physical device will give you the ability to put this API through its paces.

Unfortunately, there’s no way with PhoneGap to determine programmatically whether a device has an accelerometer except to query the accelerometer and then deal with any errors that are returned. If your application relies upon the ability to determine a device’s orientation, say for a driving game or a bubble level application, then you will likely need to abort the application when the accelerometer API returns an error.


Image Note

Not all smartphones have an accelerometer. The iPhone series of devices have always had one, but RIM didn’t add one until the BlackBerry Storm running BlackBerry Device Software 4.7.


Querying Device Orientation

The Accelerometer API allows an application to query the current orientation of the device using the following code:

navigator.accelerometer.getCurrentAcceleration(onAccelSuccess,
  onAccelFailure);

The function takes as parameters the names of two functions: the onAccelSuccess function is executed when accelerometer data is available; the onAccelFailure function is executed when there is an error retrieving accelerometer data.

Accelerometer data is passed to the onAccelSuccess function through an acceleration object, namely, the accel object shown in the following example. The object encapsulates four values reflecting the current orientation (X, Y, and Z values plus a timestamp indicating when the measurement was taken) of the device, as shown here:

function onAccelSuccess(accel) {
  xPos = accel.x;
  yPos = accel.y;
  zPos = accel.z;
  tStamp = accel.timestamp;
}

If you think about it, there are probably not a lot of use cases for just determining the device’s orientation a single time. In any game or application that truly uses orientation, determining the way the application user is orienting the device over time is much more useful than just checking it once. You could write the application so it periodically checks the device orientation manually (through repeated calls to getCurrentAcceleration), but defining an accelerometer watch (described in the next section) is a more efficient way to do this. If your application needs to perform a lot of calculation or do some work using network resources between checking orientation, then checking orientation when you want using this API would work, but you run the risk of the application not appearing responsive to the end user.

The iPhone doesn’t support the concept of determining orientation through a direct API call; instead, you must use an accelerometer watch to obtain orientation information. In this case, a call to getCurrentAcceleration simply causes the successFunction to be called and passed the accelerometer value from the last firing of an accelerometer watch.

Let’s take a look at a sample application that uses the getCurrentAcceleration API (see Example 10-1). The application creates a simple page with a button the user can click to refresh the device’s orientation data within the page.


Example 10-1

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html;
      charset=utf-8">
    <meta name="viewport" id="viewport"
      content="width=device-width, height=device-height,
      initial-scale=1.0, maximum-scale=1.0,
      user-scalable=no;" />
    <script type="text/javascript" charset="utf-8"
      src="phonegap.js"></script>
    <script type="text/javascript" charset="utf-8">
      //Accelerometer content
      var ac;
      //PhoneGap Ready variable
      var pgr = false;

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

      function onDeviceReady() {
        //Get a handle we'll use to adjust the accelerometer
        //content
        ac = document.getElementById('accelInfo'),

        //Set the variable that lets other parts of the program
        //know that PhoneGap is initialized
        pgr = true;
      }

      function getAcceleration() {
        if (pgr == true) {
          //Clear the current orientation
          ac.innerHTML = "";
          //get the current orientation
          navigator.accelerometer.getCurrentAcceleration(
            onAccelSuccess, onAccelFailure);
        } else {
          alert("Please wait, PhoneGap is not ready.");
        }
      }

      function onAccelSuccess(accel) {
        //We received something from the API, so...
        //first get the timestamp in a date object
        //so we can work with it
        var d = new Date(accel.timestamp);
        //Then replace the page's content with the
        //current acceleration retrieved from the API
        ac.innerHTML = "<b>Current acceleration</b><hr />X: " +
          accel.x + "<br />Y: " + accel.y + "<br />Z: " +
          accel.z + "<br />Timestamp: " + d.toLocaleString() +
          "<hr />Click the button to refresh.";
      }

      function onAccelFailure() {
        alert("Accelerometer error!");
      }

    </script>
  </head>
  <body onload="onBodyLoad()">
    <h1>Example 10-1</h1>
    <p>This is a PhoneGap application that measures
      Device acceleration using the Accelerometer API.</p>
    <p><input type="button"
      value="Refresh Orientation" onclick="getAcceleration();">
    </p>
    <p id="accelInfo">Nothing to see here (yet),
      click the button.</p>
    </body>
</html>


The JavaScript code within the application starts by defining a couple of variables. The application uses the ac variable to give it an easy way to update the acceleration content every time the button is clicked. Since the application user can click the button before PhoneGap has finished initializing, the pgr variable is used as a flag to allow the application to determine whether the onDeviceReady function has been executed yet. When pgr is false, the application will not attempt to query the accelerometer.

When the user clicks the button, the application executes the getOrientation function, which calls the getCurrentAcceleration API and passes in the names of the functions that are executed on success (onAccelSuccess) and failure (onAccelFailure). After accelerometer values are retrieved, the API calls the onAccelSuccess function passing in the current accelerometer values. At this point, the application updates the current page with the accelerometer data.

When you run the application on an Android device, you will see a screen similar to the one shown in Figure 10-1.

Image

Figure 10-1 Example 10-1 running on an Android device

Watching a Device’s Orientation

Instead of querying a device’s orientation through repeated calls to getCurrentAcceleration, a PhoneGap application can set up an accelerometer watch that automatically measures accelerometer data at specific intervals. To define an accelerometer watch, use the following code:

watchID = navigator.accelerometer.watchAcceleration(
  onAccelSuccess, onAccelFailure, accelOptions);

This will enable the watch and, through the accelOptions object, define options that control how the watch operates.

As with getCurrentAcceleration (described in the previous section), the names of two functions are passed to the API when the watch is created. In the example shown, the onAccelSuccess function is executed when accelerometer data is available, and the onAccelFailure function is executed when there is an error retrieving accelerometer data.

The third parameter to watchAcceleration is an optional value that defines how often the watch fires. This watch frequency is passed to the function as an object, as shown in the following example:

var accelOptions = { frequency: 1000 };

The frequency value is represented in milliseconds (1 second = 1,000 milliseconds). If the accelOptions value is omitted, the watch defaults to measuring accelerometer data every 10 seconds.

In the example call to watchAcceleration shown earlier, you’ll notice that the result of the call to watchAcceleration is assigned to the watchID variable. An application will use the watchID value later when canceling a watch when it is no longer needed, as shown in the following example:

navigator.accelerometer.clearWatch(watchID);

Let’s take a look at Example 10-2, an application that uses the watchAcceleration API. This application is an extension of Example 10-1; it removes the Refresh button and instead automatically updates accelerometer data every half second (500 milliseconds).


Example 10-2

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-type" content="text/html;
      charset=utf-8">
    <meta name="viewport" id="viewport"
      content="width=device-width, height=device-height,
      initial-scale=1.0, maximum-scale=1.0, user-scalable=no;"
     />
    <script type="text/javascript" charset="utf-8"
      src="phonegap.js"></script>
    <script type="text/javascript" charset="utf-8">

      // Accelerometer watcher ID
      var awID;
      //Orientation content
      var ac;

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

      function onDeviceReady() {
        //Get a handle we'll use to adjust the
        //acceleration content
        ac = document.getElementById('accelInfo'),

        //Accelerometer Options, read the accelerometer
        //every half second (500 milliseconds)
        var accelOptions = { frequency: 500 };

        //Add the accelerometer watcher
        awID = navigator.accelerometer.watchAcceleration(
          onAccelSuccess, onAccelFailure, accelOptions);
      }

      function onAccelSuccess(accel) {
        //Then replace the page's content with the
        //current orientation retrieved from the API
        ac.innerHTML = "<b>X:</b> " + accel.x +
          "<br /><b>Y:</b> " + accel.y +
          "<br /><b>Z:</b> " + accel.z;
      }

      function onAccelFailure() {
        alert("Accelerometer error! Clearing Watch ID");
        //Cancel the watch
        navigator.accelerometer.clearWatch(awID);
      }

    </script>
  </head>
<body onload="onBodyLoad()">
  <h1>Example 10-2</h1>
  <p>Apache PhoneGap Accelerometer Watcher</p>
  <p><b>Current Orientation</b><hr /></p>
  <p id="accelInfo">Waiting for PhoneGap to initialize.
    </p>
  <hr />
</body>
</html>


In the application’s onDeviceReady function, the application first creates the accelOptions variable that defines the watch frequency. Then, the application creates the watch using a call to watchAccelerometer. Every half second, the application calls onAccelSuccess to update the page with the latest values from the accelerometer. When you run the application on an Android device, you will see a screen similar to the one shown in Figure 10-2.

Image

Figure 10-2 Example 10-2 running on an Android device

Be sure to check the PhoneGap API documentation at http://docs.phonegap.com/en/1.1.0/phonegap_accelerometer_accelerometer.md.html for information about platform-specific oddities with this particular API.

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

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