11. Camera

The PhoneGap Camera API provides an application with the ability to work with images, either captured directly from the camera or retrieved from the device’s photo repository. When retrieving an image, the API can return either a URI pointing to the image file on the device’s file system or the base64-encoded string representing the content from the image.

The API provides a single method, navigator.camera.getPicture, which is used to retrieve an image, and a cameraOptions object that’s used to define parameters around how the image is obtained, how it’s formatted, and more.

Applications can also use the PhoneGap Capture API to capture images using the camera. Refer to Chapter 12 for more information about this API. The Camera and Capture APIs are different enough that you will want to evaluate both before selecting an option for your application.

Accessing a Picture

To obtain a picture from the device, an application should execute the following function:

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

Like other PhoneGap APIs, the call to getPicture requires that you pass in two functions that are executed on success and failure of the call. In this case, they’re the onCameraSuccess and onCameraError functions. The onCameraSuccess function is executed when an image is obtained (I’ll explain more about where the images come from and how you configure the API in the “Configuring Camera Options” section later in this chapter). The onCameraError function is executed when the user cancels the process of retrieving an image once started or when an error occurs with the process.

Example 11-1 shows the Camera API being used with its default options. According to the PhoneGap API documentation, the cameraOptions parameter is optional, but that turns out to be true for some platforms and false on others. Let’s take a look at the example application and then discuss the exceptions afterward.


Example 11-1

<!DOCTYPE html>
<html>
  <head>
    <title>Example 11-1</title>
    <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">

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

      function onDeviceReady() {
        navigator.notification.alert("onDeviceReady");
      }

      function takePhoto() {
        navigator.camera.getPicture(onCameraSuccess,
          onCameraError);
      }

      function onCameraSuccess(imageURL) {
        navigator.notification.alert("onCameraSuccess: " +
          imageURL);
      }

      function onCameraError(e) {
        navigator.notification.alert("onCameraError: " + e);
      }
    </script>
  </head>
  <body onload="onBodyLoad()">
    <h1>Example 11-1</h1>
    <p>Using the PhoneGap Camera API<br />
      <input type="button" value="Take a Picture"
        onclick="takePhoto();">
    </p>
 </body>
</html>


In this application, there’s a simple page with a button that the user clicks to take a picture using the camera. When the button is clicked, the takePhoto function is executed, which simply calls the getPicture method, passing in the onCameraSuccess and onCameraError functions.

In this example, we’re not passing in a cameraOptions object, so getPicture will just use the default options of getting the image from the camera and returning a file URI pointing to where the image was stored after it was taken. Once an image has been obtained from the camera, the onCameraSuccess function is called and passed to the URI pointing to the image file that was just created. In your applications, you’ll probably do something with the image URI, but in this case all the application does is display an alert and show the file URI passed to the function.

Figure 11-1 shows the Example 11-1 application running on an Apple iPhone.

Image

Figure 11-1 Example 11-1 running on an Apple iPhone

When you click the Take a Picture button, there’s a slight delay, and then the standard camera application will open and allow you to take a picture. The delay can be quite long, so your application may want to display a “please wait” screen before calling the API. Once a picture has been taken, iOS will display the preview window shown in Figure 11-2. At this point, you can either retake the picture or click the Use button to return to the PhoneGap application.

Image

Figure 11-2 Camera preview on iPhone

Notice from the figure that there’s no way to cancel the process at this point. If the user initiates the taking of a picture in a PhoneGap application running on iOS, there’s no way to cancel the process and not take a picture.


Counterintuitive Process

In my testing, the picture capture process was not very user friendly; only BlackBerry provided an intuitive interface for this part of the process. On BlackBerry, after you take the picture, you’re immediately returned to the PhoneGap application.

For iOS and Android, you’re presented with a preview window you can use to validate that the picture is the one you want. While this is a good thing from the user’s standpoint, the way you transition from the preview screen back to the PhoneGap application can be a counterintuitive part of the process.

On iOS, you have to click the Use button shown in Figure 11-2, which makes some sense but may not be completely clear to the user what “use” means. On some flavors of the Android OS, there’s no label on the button; you have to know to click the paper clip icon highlighted in Figure 11-6. Fortunately, some Android devices display OK, Retake, and Cancel buttons on the preview window.

Be aware of these inconsistencies and take them it into account when creating applications that leverage the camera.


On iOS, when control returns to the calling program, the application displays an alert and shows the file URI for the image file just created, as shown in Figure 11-3.

Image

Figure 11-3 Example 11-1 displaying an image file URI

One of the things to note about the iOS version of the application is that the file URI returned to the program references a temporary location that is available only to the application. If you take a look at Figure 11-3, you’ll see that the file URL refers to the following:

file://localhost/var/mobile/Applications/
169DF9CB-25D0-4EC8-85B2-380A6342E08D/tmp/photo_001.jpg

In this file URI, the file://localhost/var/mobile/Applications/ location refers to a file system area allocated to application data. The 169DF9CB-25D0-4EC8-85B2-380A6342E08D part refers to a unique identifier associated with each iOS application. The tmp folder refers to a temporary storage location allocated to the application; when the application closes, there’s a high likelihood that the temporary storage allocated to the application will be cleared, and you will lose access to the image file. If your application needs access to the image file at a later time, it will need to make a copy of the image file (using the File API described in Chapter 18) in a less volatile location before the application closes.


Absent Camera Simulators

One of the frustrating things about the iOS simulator and older Android emulators is that Google and Apple omitted camera simulators in their simulation products. When testing an application that uses the PhoneGap Camera API on one of these products, it will fail, even though the real devices support the capability. Newer Android emulators have apparently been outfitted with a camera simulator.

Fortunately, on iOS, the PhoneGap device.name property (described in Chapter 16) will accurately report whether the application is running on a physical device or a simulator. An application could detect when it’s running on a simulator and retrieve an image from the photo library instead of the camera.

For Android, there’s no direct way to determine whether the application is running on an emulator or a physical device. When testing camera functionality, the emulators won’t work; you’ll have to resort to on-device testing exclusively.


When you run Example 11-1 on an Android or BlackBerry device, you’ll have problems. In my testing, on Android it takes a picture but then crashes the PhoneGap application as it returns picture information to the application. On BlackBerry, it won’t even take the picture; you click the button, and nothing happens. Apparently the default value for Camera.DestinationType in cameraOptions for those two platforms is DATA_URL, which, because of memory limitations described elsewhere in this chapter, will cause an application to crash when a picture is taken at full resolution. This bug has been identified and should be fixed in PhoneGap 1.4.

To make the application work on Android and BlackBerry, you must modify the call to getPicture to include a simple cameraOptions object, as shown in the following example:

function takePhoto() {
  navigator.camera.getPicture(onCameraSuccess, onCameraError,
    {quality: 50,
    destinationType: Camera.DestinationType.FILE_URI }
  );
}

With that in place, you can run the application on BlackBerry and then click the Take a Picture button to see a screen similar to the one shown in Figure 11-4.

Image

Figure 11-4 Example 11-1 taking a picture on a BlackBerry device

When you click the camera button at the bottom middle of the screen, the captured image will be returned to the PhoneGap application, as shown in Figure 11-5. Notice from the figure that the image file is stored in the default BlackBerry photo storage location, so unlike iOS, any pictures taken by the application will be available after the application terminates.

Image

Figure 11-5 Example 11-1 camera image file location on BlackBerry

If you do not want your application’s photos to be left lying around after your application closes, you will need to manually delete the image file(s) once your application is through processing them. The application can use the PhoneGap File API (described in Chapter 18) to delete the file after the application is done with it.

Figure 11-6 shows Example 11-1 running on an Android device. In this case, the picture has already been taken, and what’s shown is the picture preview window that the Android OS provides users. The frustrating part of what’s shown in the figure is that from the user’s standpoint, it’s hard to know what to do next here. It’s likely clear to the user that he is previewing a picture he just took (he did just take the picture after all), and it’s possible that he will figure out that he can take another picture by clicking the camera image and can delete the image by clicking the trash can icon. The purpose of the paper clip icon, highlighted in Figure 11-6, is unclear, but when you click it, information about the selected image is returned to the PhoneGap application.

Image

Figure 11-6 Example 11-1 image preview on Android

You might be asking yourself, what do I do with this image file URI once I get it back from the camera? Well, it’s a file pointer pointing to an image file, so once the application knows where the file is, it can read from the file, copy it somewhere else (using the PhoneGap File API, described in Chapter 18), or even pass the file URI to the PhoneGap application’s UI to display the image within the application.

Example 11-2 is a slightly modified version of Example 11-1. In this version, when the image URI is returned to the application, an HTML image tag is written to the index.html page so the captured image will appear on the screen.


Example 11-2

<!DOCTYPE html>
<html>
  <head>
    <title>Example 11-2</title>
    <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">

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

       function onDeviceReady() {
         navigator.notification.alert("onDeviceReady");
       }

       function takePhoto() {
         navigator.camera.getPicture(onCameraSuccess,
           onCameraError,
           {quality : 50,
           destinationType : Camera.DestinationType.FILE_URI});
       }

       function onCameraSuccess(imageURL) {
         //Get a handle to the image container div
         ic = document.getElementById('imageContainer'),
         //Then write an image tag out to the div using the
         //URL we received from the camera application.
         ic.innerHTML = '<img src="' + imageURL +
           '" width="50%" />';
       }

       function onCameraError(e) {
         console.log(e);
         navigator.notification.alert("onCameraError: " + e +
           " (" + e.code + ")");
       }
     </script>
   </head>
   <body onload="onBodyLoad()">
     <h1>Example 11-2</h1>
     <p>
       Using the PhoneGap Camera API
       <br />
       <input type="button" value="Take a Picture"
         onclick="takePhoto();">
       <div id="imageContainer"></div>
     </p>
  </body>
</html>


The major change is in the onCameraSuccess function shown here. It’s been rewritten so it grabs the content of the imageContainer <div> and then replaces it with an <img> tag that references the file URI returned by the getPicture function.

function onCameraSuccess(imageURL) {
  //Get a handle to the image container div
  ic = document.getElementById('imageContainer'),
  //Then write an image tag out to the div using the
  //URL we received from the camera application.
  ic.innerHTML = '<img src="' + imageURL + '" width="50%" />';
}

Figure 11-7 shows the application running on an Android device.

Image

Figure 11-7 Example 11-2 running on an Android device

Configuring Camera Options

Now that you know how to take pictures using the camera, let’s talk about options you can use to configure how the process works. As you may remember from the previous section, when calling getPicture, a developer can pass in a cameraOptions object that defines parameters controlling how the picture is obtained. The cameraOptions object supports the following properties:

quality

destinationType

sourceType

allowEdit

encodingType

targetWidth

targetHeight

mediaType

Each of these options will be described in greater detail in the following sections. Like with many other features of PhoneGap APIs, certain API options (such as allowEdit in the Camera API) apply on only a limited number of mobile platforms.

Here’s an example of a fully configured cameraOptions object you could use in one of your PhoneGap applications:

var cameraOptions = { quality : 75,
  sourceType : Camera.PictureSourceType.CAMERA,
  destinationType : Camera.DestinationType.FILE_URI,
  allowEdit : true,
  encodingType: Camera.EncodingType.JPEG,
  targetWidth: 1024,
  targetHeight: 768 };

When passed to the getPicture function, this cameraOptions object tells getPicture to get the picture from the camera (sourceType), return a file URI that points to the image file captured (destinationType), allow the user to edit the picture before returning it to the program (allowEdit), return the picture as a .jpeg file (encodingType), configure the encoded image file to use 75% image quality (quality), and set the image dimensions to 1024 by 768 pixels (targetWidth and targetHeight).

Now let’s describe each of the cameraOptions properties in greater detail.

quality

When working with smartphone cameras, higher-resolution optics in the camera lens plus limited memory storage and network bandwidth available to devices drove the need to be able to compress images so they took up less storage space and transmission bandwidth. As part of this compression process, standards such as the JPEG specification included support for using image quality to control compression rates when an image file is being saved. By using different image quality settings, defined as percentages, you can dramatically affect the physical size of an image file.

An image quality of 100% shows the image at its full capacity, with no reduction in image quality, and gives you the best possible picture. As you reduce the image quality, you will see some degradation in clarity in the image, but for most purposes it will be acceptable—only smaller in file size.

The quality parameter allows a developer to specify the percentage image quality for a picture captured using the Camera API. In most cases, you will use values from 50% to 100% for your images. This is not so much because you care about image quality, but more because you need to reduce image quality in order to reduce image file size.

As you’ll see in the following section, developers can have an image file URI returned from a call to getPicture or the actual raw, base64-encoded image file data. Using the image file URI is easy; it’s just a file pointer, and you’ve already seen examples here of how to use it in your applications. When obtaining raw image data from getPicture, you have to deal with the fact that the device and the JavaScript interpreter on the device have limits on how much data they can process. As newer smartphones get higher and higher resolution cameras, you must reduce image quality so that a PhoneGap application can successfully process the returned image data. When processing raw data from a high-resolution picture at 100% quality, you’d be processing a huge string, and the application might just crash without telling you why (like we saw when using default options for cameraOptions in Example 11-1). When you reduce image quality, you reduce the amount of data the application must process and increase the likelihood it will actually work.

Unfortunately, there is no guideline I can give you for how much you have to reduce your image quality to guarantee success. You’ll just have to guess and test and know that the value may differ on different platforms and even on different devices on the same platform. The folks working on PhoneGap recommend using 50% image quality (or lower) when working with raw image data.

To configure a cameraOptions object to use a picture quality of 50%, use the following code:

quality : 50

According to the PhoneGap documentation, this option is ignored on the BlackBerry platform.

destinationType

When capturing an image using getPicture, applications will use destinationType to control whether the image information is returned as a file URI pointing to the image file stored in device memory:

destinationType: Camera.DestinationType.FILE_URI

To receive the picture’s image data as a base64-encoded string value, use the following:

destinationType: Camera.DestinationType.DATA_URL

Working with file URIs is easy, as shown in Example 11-2. The application has a file pointer than can be manipulated within the application either by populating an HTML img tag or when using the File API to copy the file to another location. Once you know where the file is, accessing the image file is a simple process.

Figure 11-8 shows the output from Example 11-1 when a destinationType of Camera.DestinationType.DATA_URL is used. As you can see from the figure, what you have to work with is just a huge string, which, as mentioned in the previous section, may cause memory overflow and crash your program if the string is too big.

Image

Figure 11-8 Raw image data rendered in an Android alert dialog

Using this raw image data, you can still render the picture in the UI, but you’re more likely going to want to either store the data in a database or upload the data to a file server. There’s just too much risk in trying to manipulate the image on the mobile device.

sourceType

The sourceType parameter is used to define where getPicture gets its picture from. When sourceType is omitted, getPicture will simply use the camera (Camera.SourceType.CAMERA) to grab the picture. Applications can specify to use the device’s photo library using the following:

sourceType : Camera.SourceType.PHOTOLIBRARY

To retrieve photos from a saved photo album, use the following:

sourceType : Camera.SourceType.SAVEDPHOTOALBUM

On most platforms, specifying a sourceType of SAVEDPHOTOALBUM or PHOTOLIBRARY does essentially the same thing. As shown in Figure 11-9, when the application makes a call to getPicture, the device will open the photo library application and allow the user to first select a photo album and then select a single picture before returning the selected picture to the PhoneGap application.

Image

Figure 11-9 Photo gallery application on Android

On iOS devices, the two operate differently. When a sourceType of PHOTOLIBRARY is specified, the application behaves similarly to what is highlighted in Figure 11-9. When specifying a sourceType of SAVEDPHOTOALBUM, the application will open the standard iOS Camera Roll photo library and allow the user to select a picture from there.

According to the PhoneGap documentation, this option is ignored on the BlackBerry platform.

allowEdit

An iOS application can use the allowEdit option to instruct getPicture to allow the user to edit the selected image before returning it to the PhoneGap application. To configure a cameraOptions object for this option, use the following:

allowEdit : true

Once enabled in an application, after the camera takes a picture, the device will display a screen similar to the one shown in Figure 11-10. At this point, the user can pinch, prod, and slide the picture around to fit the portion of the image they want to capture into the reticle shown in the figure. When the user clicks the Choose button, the edited picture is returned to the calling PhoneGap application.

Image

Figure 11-10 Picture editing on the iPhone

encodingType

A PhoneGap application uses the encodingType cameraOption to tell getPicture what kind of picture to take. Supported options are JPEG and PNG, with JPEG being the default on most, if not all, platforms. To configure getPicture to return a JPEG file, use the following:

encodingType: Camera.EncodingType.JPEG

To use PNG files, use the following:

encodingType: Camera.EncodingType.PNG

This option is not supported on all platforms; refer to the PhoneGap documentation for specifics.

targetHeight and targetWidth

The targetHeight and targetWidth parameters control the height and width of the image obtained using getPicture. You can set either targetHeight or targetWidth, and the image will be scaled accordingly. If you specify both, the image will be scaled to the one that results in the smallest aspect ratio. Either way, the aspect ratio will be maintained.

Since there’s no way to programmatically determine the camera resolution or the supported aspect ratio before taking a picture, there is therefore no way to accurately set these values within an application without guessing or direct testing on each supported device.

To define a cameraOptions object that specifies targetHeight and targetWidth for the image, use the following code:

targetHeight: 100, targetWidth: 100

mediaType

Since many modern smartphones can typically store multiple media types in a photo library or photo library, the PhoneGap Camera API supports the addition of a mediaType value in the cameraOptions object in cases where the sourceType is set to PHOTOLIBRARY or SAVEDPHOTOALBUM. The parameter supports the following options:

DEFAULT: Returns image information using the format specified in the destinationType value

ALLMEDIA: Allows selection from all media types

PICTURE: Allows the selection of photographs only

VIDEO: Allows selection of video files only

When the option for VIDEO is selected, only a file URI will be returned to the calling program. Returning the raw video image data in a JavaScript String variable would certainly overload the JavaScript interpreter included in the browser and would most likely crash the application.

Dealing with Camera Problems

As with any computer or smartphone development, there are lots of places where things can go wrong. The purpose of this section is to highlight some of the ways you can tell what’s going on when the Camera API fails.

When the onCameraError function fires, the Camera API passes in an error object that can be queried to determine the cause of the error. As shown in Figure 11-11, the error is a simple text message that tells what happened. In this case, the user clicked the Cancel button in Figure 11-10, so there’s no image information to return to the PhoneGap application.

Image

Figure 11-11 An example of the onCameraError function firing in a PhoneGap application

When the application runs on a device that doesn’t have a camera, you will see an error similar to the one shown in Figure 11-12.

Image

Figure 11-12 An example of the onCameraError function firing on an iOS simulator

If your application is running on a device that doesn’t have a camera, it’s likely, but not guaranteed, that the onCameraError function will be executed by the Camera API. If an application fails and you’re not sure why, don’t forget that the console log may contain information that can help. Figure 11-13 shows a portion of the iOS console with Example 11-1 running. Notice that when the I click the Take a Picture button, the console logs an error indicating that source type 1 (the camera) is not available.

Image

Figure 11-13 Using the console to debug camera issues

This is one of those weird examples where even though the device supports a camera, Apple hasn’t decided it’s important enough to include that functionality in the device simulator. In this case, to be able to test on the iOS simulators, your application will need to check to see which device it’s running on and use a photo library rather than the camera in cases where the camera is not available.

If your application seems to be running properly but when you take a picture nothing happens or the application crashes, it’s likely caused by the application returning raw camera data (rather than a file URI) and the device isn’t capable of processing a string of that size. When this happens, try cranking down image quality (using the cameraOption quality setting) to 50% or less to see whether this fixes the problem. If it does, then you’re going to have to do some work to determine the optimal image quality setting for your application and the devices it’s running on.

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

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