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.
1
2
3
4
5
6
7
8
9
10
11
12
|
<!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.
1
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<!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:
1
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
//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.
1
2
3
|
< 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.
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.
1
2
3
4
5
|
var previousReading = { x: null , y: null , z: null } |
Next, we start the watchAcceleration function.
1
|
navigator.accelerometer.watchAcceleration(); |
On Success of getting the acceleration, we will set a couple of variables that will help us detect a shake.
1
2
|
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.
1
2
3
4
5
6
7
8
9
|
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.
1
2
3
4
5
|
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.
1
2
3
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
<!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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
<!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;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:
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:
1
|
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:
1
2
3
4
5
6
7
8
|
//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:
1
|
navigator.geolocation.getCurrentPosition(success, error); |
And then we can do something like the following:
1
2
3
4
5
6
7
8
9
10
11
12
|
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
<!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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<!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:
Like the accelerometer, geolocation also has a clearWatch
method to stop watching for changes, which you can use with the following code:
1
|
navigator.geolocation.clearWatch(watchID); |
From : http://mobile.tutsplus.com/tutorials/phonegap/phonegap-from-scratch-device-apis/
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!