Advertisement

PhoneGap From Scratch: Device APIs

by
Student iconAre you a student? Get a yearly Tuts+ subscription for $45 →
This post is part of a series called PhoneGap From Scratch.
PhoneGap From Scratch: Introduction
PhoneGap From Scratch: App Template

Want to learn how to use PhoneGap, but don't know where to get started? Join us as we put together “Sculder”, not only a tribute to an excellent science fiction TV series, but a fully-fledged native mobile application for the believer in you!

In our first part we looked at setting up our development environment and getting PhoneGap up and running on the iOS and Android SDKs. In this second part of our PhoneGap series, we are going to look at some of the device APIs that PhoneGap gives us access to and discuss how we might use them.


Setting Up Our Project

For this part of the series we are just going to be looking at some of the functionality of PhoneGap, so we will just set up a test for now.

Go ahead and set you project up in whatever environment you have chosen: Xcode for iOS or Eclipse for Android. I will point out any differences between the two as we go along if it becomes necessary.

We'll start off with some basic HTML and include the Phongap.js file. If you have created your project with Xcode, this is pretty much the basic HTML that is created.

<!DOCTYPE html>
<html>
  <head>
    <title>Acceleration</title>

    <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>

  </head>
  <body>
    
  </body>
</html>

Deploying to a Test Device

Throughout this part of the series we are going to want to be able to test on at least one actual device as the simulator has limitations when it comes to device sensors such as the accelerometer and the camera. To get an iOS device up and running as a test device you need to have a paid developer account, then when you connect your device to you computer and run Xcode, you have the option to make that phone a development phone. Go through the setup and now when you choose to build and run your app, you can select your device from the drop down menu.

For Android, it's pretty much the same except you do this in Eclipse. Connect your phone and make sure that you have it in debug mode VIA USB (in the phone settings) and then when you want to run your app, select Run As Android App.

Let's take a look at some of the device sensor basics.


The Accelerometer API

The Accelerometer provides feedback for the devices motion across all three axes. We have a couple of methods for the Accelerometer within PhoneGap that are getCurrentAcceleration, watchAcceleration and clearWatch

There are also some arguments to pass through on the Accelerometer method. accelerometerSuccess, accelerometerError and accelerometerOptions.

We use our first method, accelerometer.getCurrentAcceleration, as follows.

	navigator.accelerometer.getCurrentAcceleration(accelerometerSuccess, accelerometerError);

The current acceleration is returned using the accelerometerSuccess function and all the data we need is in the acceleration object that we pass back into our success function. Lets get an example up and running. Take our basic layout we set up in the beginning of this part and let's add to it.

<!DOCTYPE html>
<html>
  <head>
    <title>Acceleration</title>

    <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>
    <script>
    	//first we want to wait for PhoneGap to load
    	document.addEventListener("deviceready", loaded, false)
    	//PhoneGap is loaded
    	function loaded(){
    		navigator.accelerometer.getCurrentAcceleration(onSuccess, onError);
    	}

    	//Get the current Acceleration data if Successful
    	function onSuccess(acceleration){
    		alert('Acceleration X: ' + acceleration.x + '\n' +
              'Acceleration Y: ' + acceleration.y + '\n' +
              'Acceleration Z: ' + acceleration.z + '\n' +
              'Timestamp: '      + acceleration.timestamp + '\n');
    	}

    	// alert if there is an error
    	function onError(){
    		alert("Error");
    	}

    </script>

  </head>
  <body>
    
  </body>
</html>

When you run this in a simulator or device, you will be greeted with a single alert on load. What we need to do is watch the Acceleration at intervals and then output the data. We can do this with the watchAcceleration method. We use it with the following:

var watchID = navigator.accelerometer.watchAcceleration(Success, Error, [Options]);

The watchID is a reference that we can attach our options to and also a way that we can use when using the clearWatch method.

Let's go ahead and replace our older JavaScript with the following:

	//wait for PhoneGap to load
	document.addEventListener("deviceready", loaded, false);

    // PhoneGap is ready
    function loaded() {
        startWatch();
    }

    // Start watching the acceleration

    function startWatch() {

        // Update acceleration every 3 seconds
        var options = { frequency: 3000 };

        watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
    }

    // Stop watching the acceleration
    function stopWatch() {
        if (watchID) {
            navigator.accelerometer.clearWatch(watchID);
            watchID = null;
        }
    }

    // Success
    function onSuccess(acceleration) {
        var element = document.getElementById('accelerometer');
        element.innerHTML = 'Acceleration X: ' + acceleration.x + '<br />' +
                            'Acceleration Y: ' + acceleration.y + '<br />' +
                            'Acceleration Z: ' + acceleration.z + '<br />' +
                            'Timestamp: '      + acceleration.timestamp + '<br />';
    }

     // Error
    function onError() {
        alert('onError!');
    }

As you can see, we pass in a frequency option into the watch method. This is in milliseconds, so every 3 seconds that method will be fired again, and on success we will update the HTML of an element with the ID of the accelerometer. We just need to include that element in our current HTML.

<body>
    <div id="accelerometer">Waiting for accelerometer...</div>
</body>

Now, if you go ahead and load up the app, you will see the accelerometer data change.

PhoneGap From Scratch

If you are using the simulators rather than actual devices, you will not see any change in the accelerometer output.

So, that's it for accessing the accelerometer device API. Now let's take a look at how to use it to detect a shake in PhoneGap.

Shake Events

To detect a shake using PhoneGap we are going to get rid of our onSuccess function and rewrite our startWatch function. In order to know if the device has been shaken, we are going to have to know what the previous orientation was in order to compare that to the current orientation. We do this by setting a variable at the beginning of the startWatch function.

var previousReading = {
	x: null,
    y: null,
    z: null
}

Next, we start the watchAcceleration function.

navigator.accelerometer.watchAcceleration();

On Success of getting the acceleration, we will set a couple of variables that will help us detect a shake.

var changes = {},
bound = 0.2;

Now we can compare the previous acceleration to the current acceleration and if it goes beyond what we have set our bound variable too, then we can fire our shaken function.

if (previousReading.x !== null) {
	changes.x = Math.abs(previousReading.x, acceleration.x);
	changes.y = Math.abs(previousReading.y, acceleration.y);
	changes.z = Math.abs(previousReading.z, acceleration.z);
}

if (changes.x > bound && changes.y > bound && changes.z > bound) {
	shaken();
}

We can then set the previous reading to the current reading for the next time round.

 previousReading = {
  x: acceleration.x,
  y: acceleration.y,
  z: acceleration.z
 }

Finally, let's not forget to write a "shaken" function to actually handle the shake. For now it will just alert a message.

function shaken(){
	alert("Shaken");
}

You'll need to remember to add you error handler and frequency to the end on the watchAcceleration method.

Your final code should now look something like this:

<!DOCTYPE html>
<html>
    <head>
        <title>Acceleration</title>
        
        <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>
        <script type="text/javascript" charset="utf-8">
            
            // The watch id references the current `watchAcceleration`
            var watchID = null;
            
            //wait for PhoneGap to load
            document.addEventListener("deviceready", loaded, false);
            
            // PhoneGap is ready
            function loaded() {
                startWatch();
            }
            
            // Start watching the acceleration
            
            function startWatch() {
                
                var previousReading = {
                    x: null,
                    y: null,
                    z: null
                }
                
                navigator.accelerometer.watchAcceleration(function (acceleration) {
                  var changes = {},
                  bound = 0.2;
                  if (previousReading.x !== null) {
                      changes.x = Math.abs(previousReading.x, acceleration.x);
                      changes.y = Math.abs(previousReading.y, acceleration.y);
                      changes.z = Math.abs(previousReading.z, acceleration.z);
                  }
                  
                  if (changes.x > bound && changes.y > bound && changes.z > bound) {
                  	shaken();
                  }
                  
                  previousReading = {
                  x: reading.x,
                  y: reading.y,
                  z: reading.z
                  }
                  
                  }, onError, { frequency: 2000 });
            }
            
            function shaken(){
                alert("Shaken");
            }
            
            // Error
            function onError() {
                alert('onError!');
            }
            
            </script>
    </head>
    <body>
    </body>
</html>

I found that the bound of 0.2 was a pretty good one, but you might want to try increasing it after testing. We now have covered what can be achieved with the accelerometer data and how to capture it, so let's take a look at the camera.


The Camera API

The camera is probably one of the most used functionalities on smart phones today, particularly with the camera resolution on most phones quickly catching up to more standard point-and-shoot versions. Thankfully, PhoneGap gives us a pretty easy way to capture images from the device's camera and quickly incorporate those images into our application.

The method we are going to be using is camera.getPicture() and just like the accelerometer its called in pretty much the same way and takes three parameters. The method signatures looks something like this: navigator.camera.getPicture( cameraSuccess, cameraError, [ cameraOptions ] ). As you will see, there are a lot more options to be considered when dealing with the device camera than when dealing with the accelerometer.

The optional parameters you can pass through are as follows:

  • quality
  • destinationType
  • sourceType
  • allowEdit
  • encodingType
  • targetWidth
  • targetHeight

As you might have guessed, quality is the quality that the image is saved at, this takes a number from 0 - 100. The destinationType variable is the format of the returned image. DATA_URL is a base64 encoded string and FILE_URI is an actual image URI (jpeg/png). The sourceType parameter is where you want to get the source image, which can be from the PHOTOLIBRARY, CAMERA or SAVEDPHOTOALBUM. The allowEdit option allows for the image to be edited before being saved. EncodingType defines the encoding of the returned image when using FILE_URI, from which you can use either JPEG or PNG. targetWidth and targetHeight is what the image will be scaled to with aspect ratio maintained. Finally, there is MediaType which only functions when selecting SAVEDPHOTOALBUM and where you might want to define what the user can select out of PICTURE, VIDEO or ALLMEDIA.

So, let's get our camera started. First we are going to have a button that when clicked will start up our camera. Then when the photo is taken, we will return the image base64 encoded as a thumbnail. The source code looks like this:

<!DOCTYPE html>
<html>
    <head>
        <title>Capture Photo</title>
        
        <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>
        <script type="text/javascript" charset="utf-8">

        	var pictureSource,
        		destinationType
            
            document.addEventListener("deviceready",loaded,false);
            
            function loaded() {
                pictureSource=navigator.camera.PictureSourceType;
                destinationType=navigator.camera.DestinationType;
            }
            
            function getPhoto(imageData) {
                var smallImage = document.getElementById('smallImage');
                
                
                smallImage.style.display = 'block';
                
                
                smallImage.src = "data:image/jpeg;base64," + imageData;
            }
            
            function capturePhoto() {
                navigator.camera.getPicture(getPhoto, onFail, { quality: 50 });
            }
           
            
            function onFail(message) {
                alert('Failed because: ' + message);
            }
            
            </script>
    </head>
    <body>
        <button onclick="capturePhoto();">Capture Photo</button> <br>
        <img style="display:none;width:60px;height:60px;" id="smallImage" src="" />
    </body>
</html>

As before, we wait for PhoneGap to be loaded. When loading is complete, we can set the options for the destinationType and the sourceType, by default these are set to CAMERA and DATA_URL. When the button is clicked, we fire the capturePhoto function. Upon success, capturePhoto starts our getPhoto function. Our function receives the image data in the format we specified, and we can do with that what we want. All we are really doing is getting an HTML element specified and putting our data in the src of that element.

Run and Test your code on your device and after you have taken a picture and tested you should have something that looks like below:

PhoneGap From Scratch

It's also possible to edit the photo after capture, all we have to do is pass through the allowEdit : true parameter in the options, after the photo has been taken. It will move into the edit screen, where you can zoom and crop the photo. We can use the following code when we capture the image:

navigator.camera.getPicture(getPhoto, onFail, { allowEdit: true });

There are some quirks to the allowEdit option worth noting. Currently, this only works in iOS and is ignored in Blackberry, Android, Palm, and Windows 7.

If we wanted to get a photo from the photo album, or other storage (such as localstorage) we would use pictureSource.PHOTOLIBRARY.

Those are pretty much the basics we need to get up and running with the Camera in PhoneGap. Have a play around with it and try out some things with different image qualities, types, and sizes.


Storage APIs

It's possible that we might want to store the photo taken somewhere other than the photo album on the device. In fact, this is highly likely to be the case. We will probably also want to store other information. There are a few ways to go about this to use the device storage, one of them is to use WebSQL, the other is using WebStorage - both as defined by the W3C. You could also send the data to a remote server if you wanted to serve it in a cloud app (Instagr.am), or you could go a step further and use Lawnchair or PersistenceJS

.

I personally prefer the WebStorage method and for this project it's perfect.

We can make use of WebStorage with the following syntax:

	//Store the data
	window.localStorage.setItem("key", "value");
	//retrieve the data
	var value = window.localStorage.getItem("key");
	// value is now equal to "value"

	// remove the value
	window.localStorage.removeItem("key");

With this basic syntax, we have the ability to store the base64 encoded image in local storage and retrieve it when we need to.


The Geolocation API

Geolocation provides location information of the device. Many devices can already use the browsers ability to use the Geolocation API and if you use the PhoneGap's implementation, it uses this if available.

PhoneGap's Geolocation has 3 methods, getCurrentPosition, watchPosition and clearWatch. The getCurrentPosition method returns the device's current location with a position object that contains the properties for:

  • latitude
  • longitude
  • altitude
  • accuracy
  • altitudeAccuracy
  • heading
  • speed

The basic use of the Geolocation functionality should look pretty familiar by now:

	navigator.geolocation.getCurrentPosition(success, error);

And then we can do something like the following:

function onSuccess(position) {
	var el = document.getElementById('location');
    	el.innerHTML = 
    	'Latitude: ' 		   + position.coords.latitude              + '<br />' +
        'Longitude: '          + position.coords.longitude             + '<br />' +
        'Altitude: '           + position.coords.altitude              + '<br />' +
        'Accuracy: '           + position.coords.accuracy              + '<br />' +
        'Altitude Accuracy: '  + position.coords.altitudeAccuracy      + '<br />' +
        'Heading: '            + position.coords.heading               + '<br />' +
        'Speed: '              + position.coords.speed                 + '<br />' +
        'Timestamp: '          + new Date(position.timestamp)          + '<br />';
}

Your full code should look something like the following:

<!DOCTYPE html>
<html>
    <head>
        <title>Geolocation</title>
        
        <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>
        <script type="text/javascript" charset="utf-8">
            
            document.addEventListener("deviceready", loaded, false);
            
            function loaded() {
                navigator.geolocation.getCurrentPosition(success, error);
            }
            
            function success(position) {
                var element = document.getElementById('geolocation');
                element.innerHTML = 'Latitude: ' + position.coords.latitude     + '<br />' +
                'Longitude: '			+ position.coords.longitude             + '<br />' +
                'Altitude: '           	+ position.coords.altitude              + '<br />' +
                'Accuracy: '           	+ position.coords.accuracy              + '<br />' +
                'Altitude Accuracy: '  	+ position.coords.altitudeAccuracy      + '<br />' +
                'Heading: '            	+ position.coords.heading               + '<br />' +
                'Speed: '              	+ position.coords.speed                 + '<br />' +
                'Timestamp: '          	+ new Date(position.timestamp)          + '<br />';
            }
            
            function error(error) {
                alert(error.message);
            }
            
            </script>
    </head>
    <body>
        <p id="geolocation">Finding geolocation...</p>
    </body>
</html>

This will give you the position information the moment that the success function is fired. If we want to constantly watch the geolocation of the device we use the navigator.geolocation.watchPosition method in place of navigator.geolocation.getCurrentPosition, passing it the frequency that we'd like to update on. Our code should now look something like this:

<!DOCTYPE html>
<html>
    <head>
        <title>Geolocation</title>
        
        <script type="text/javascript" charset="utf-8" src="PhoneGap.js"></script>
        <script type="text/javascript" charset="utf-8">
            
            document.addEventListener("deviceready", loaded, false);
            
            var watchID = null;
            
            function loaded() {
                watchID = navigator.geolocation.watchPosition(success, error, { frequency: 3000 });
            }
            
            
            function success(position) {
                var element = document.getElementById('geolocation');
                element.innerHTML = 'Latitude: ' + position.coords.latitude     + '<br />' +
                'Longitude: '			+ position.coords.longitude             + '<br />' +
                'Altitude: '           	+ position.coords.altitude              + '<br />' +
                'Accuracy: '           	+ position.coords.accuracy              + '<br />' +
                'Altitude Accuracy: '  	+ position.coords.altitudeAccuracy      + '<br />' +
                'Heading: '            	+ position.coords.heading               + '<br />' +
                'Speed: '              	+ position.coords.speed                 + '<br />' +
                'Timestamp: '          	+ new Date(position.timestamp)          + '<br />' +
                '<hr>' + element.innerHTML;
            }
            
            function error(error) {
                alert(error.message);
            }
            
            </script>
    </head>
    <body>
        <p id="geolocation">Finding geolocation...</p>
    </body>
</html>

If you run your app now, you should end up with an app that looks like this:

PhoneGap From Scratch

Like the accelerometer, geolocation also has a clearWatch method to stop watching for changes, which you can use with the following code:

	navigator.geolocation.clearWatch(watchID);

With that, we now have the knowledge to use the geolocation API in PhoneGap for our application. We might just want to record our location at any given time and store it locally or remotely, we might also want to record our movement over time and store that. Whatever we want to do with it, we now know how to get that information.


PhoneGap Plugins

On top of the functionality that PhoneGap offers out of the box, there is a hoard of Plugins available for PhoneGap for things like a date picker, file uploader, and Paypal. As we won't be using any plugins in this app, going over the use and installation of the plugins is beyond the scope of this series, but you should know what options you have when working wit PhoneGap so be sure to check out the GitHub project for plugins and the wiki for info on how to get up and running with them. If you would like to see a separate tutorial on using PhoneGap plugins and even writing your own, be sure to let us know in the comments!


Conclusion

While we haven't started development on an application, we are now up and running with PhoneGap, able to test on a device and simulator, have a good grasp of how PhoneGap's APIs work, and how we might use the API in an app. In the next part, we will begin building Sculder, our sample application!

Advertisement