Using the Android CameraToday we’re going to talk about using the camera in your applications as we get close to wrapping up the 31 Days of Android.  There are two different ways you can use the camera in your applications:  via an Intent or directly using the camera hardware.  If you use an Intent, you’re relying on a different application to handle taking the picture or video and “handing it back” to your app.  Today we’ll look at how you can get pictures from the camera with an Intent as well as how to get some more information from the camera.  You can download the starter code we’ll use today here.

 

Manifest Changes

Before you can use the camera, you need to add a permission to the manifest file.  This will be listed when users go to install your application so that they will know your app will use their camera.

<uses-permission android:name="android.permission.CAMERA" />

Technically this is the only change you need to make to your manifest.  However, you can also specify that the camera is a feature of your app by adding this to your manifest file:

<uses-feature android:name="android.hardware.camera" android:required="false" />

Note that at the end you’re saying that the camera isn’t required to install your app.  If you set this to true or remove it, then users wont’ be able to install your app if they don’t have a camera.  Today I think all phone and tablets that come out come with cameras though it’s entirely possible devices without cameras could access the Android Marketplace (i.e. Google TV boxes) so if you’re building an application that completely depends on having a camera, you may want to require it in your manifest file.  If you aren’t going to require a camera but want to check to see if a camera is available you can use the PackageManager to find out:

PackageManager packageManager = getPackageManager();
boolean doesHaveCamera = 
        packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);

If hasSystemFeature returns false than you know the device doesn’t have a camera available.  While not necessary for camera usage, you should also add the permission for external storage to your manifest file:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Later on you’ll write your images to external storage so it’s easier to take care of this now.

 

Using the Camera with Intents

Just starting the camera is actually remarkably easy.  If you’ve used intents before to launch other applications, this will look very familiar:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(intent, 100);

Running this code will launch the camera app.  The default camera app will allow you to take a picture and then either approve it, retake it, or cancel.  Once you’ve said you’re done or cancelled, you’ll return to your application.  At this point, nothing else is happening.  When the camera app returns to your application, it sends an Intent over which you can get at in the onActivtiyResult method.  Let’s say you want to take the image that was taken and display it back to the user.  First you’ll need to open the res/layout/main.xml and add an ImageView under the last button:

<Button
    android:id="@+id/button2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button" />
<ImageView
    android:id="@+id/imageView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

Now in your onActvitiyResult method, you can put the image that was taken in as the ImageView:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
    Bundle extras = intent.getExtras();
    ImageView imageView1 = (ImageView) findViewById(R.id.imageView1);
    imageView1.setImageBitmap((Bitmap) extras.get("data"));
}

The camera returns the image data as an extra named “data” and once you get it you can cast it to a Bitmap to use it as an image.  From there it’s easy to just set the ImageView’s ImageBitmap property.  Now when you run your app and take a picture, you should see the picture show up on beneath the button:

setting imageview to picture

If you close down your application and run the gallery application, you’ll see that the images you took for your app are there.  This means that it’s storing images in the default location.  If you want to, you can specify a place to put the images you capture.  This is done by putting an extra on your image capture Intent before you send it:

Intent intent = new Intent(
        MediaStore.ACTION_IMAGE_CAPTURE);
// Get our fileURI
Uri fileUri = getOutputMediaFile();
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri); 
startActivityForResult(intent, 100);

The fileURI you are passing in is created from a File you create on the disk.  You could write these files to any location that you have access to.  If you wanted to keep them private to your application, you could pass in a URI for your app’s data directory.  If you wanted to store them externally on the SD card, you could do something like this:

private Uri getOutputMediaFile() throws IOException {
    File mediaStorageDir = new File(
            Environment.getExternalStoragePublicDirectory(
                    Environment.DIRECTORY_PICTURES),"DayTwentyNine");
    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss")
            .format(new Date());
    File mediaFile;
    mediaFile = new File(mediaStorageDir.getPath() + File.separator
            + "IMG_" + timeStamp + ".jpg");
 
    if (mediaFile.exists() == false) {
        mediaFile.getParentFile().mkdirs();
        mediaFile.createNewFile();
    }
    return Uri.fromFile(mediaFile);
}

Here you’re creating a file in the SD card’s pictures directory, under a subfolder named “DayTwentyNine”.  The name of the file is “IMG_” followed by the timestamp.  Now if you run your app, it’s going to crash when it returns from taking the picture.  The reason is that when you pass in the EXTRA_OUTPUT extra, the camera doesn’t return the image data because it knows you know where it’s going to write the file to.  Once the camera returns, in onActivityResult you can check to make sure it was a success and then do whatever you need with the picture.  If you wanted to be able to handle the picture being returned or not being returned, your onActivityResultmethod might look like this:

@Override
protected void onActivityResult(int requestCode, int resultCode,
        Intent intent) {
    if (requestCode == 100) {
        if (resultCode == RESULT_OK) {
            if (intent == null) {
                // The picture was taken but not returned
                Toast.makeText(
                        getApplicationContext(),
                        "The picture was taken and is located here: "
                                + fileUri.toString(), Toast.LENGTH_LONG)
                        .show();
            } else {
                // The picture was returned
                Bundle extras = intent.getExtras();
                ImageView imageView1 = (ImageView) findViewById(R.id.imageView1);
                imageView1.setImageBitmap((Bitmap) extras.get("data"));
            }
        }
    }
}

Here you’re still setting the ImageView if the data is returned, otherwise you’re just showing a Toast with the file location.  You could alternatively open that file and display it in the ImageView if you wanted. 

 

Checking Megapixels

One scenario I’ve found myself in at work that took some research to figure out was knowing how many megapixels the camera was.  It seems like there should be an easy to use built-in way to get a rating on the camera on the device but, as of yet, I haven’t found anything.  Thankfully it’s not very difficult to calculate how many megapixels the camera supports.  First you need to make a connection to the camera hardware, and then you can use the getSupportedPictureSizes method to get a list of supported picture sizes.  This gives you a list of heights and widths which you can multiply to get picture resolutions in pixels.  You can use this number to find megapixels.  This code will go through these lists and round up or down appropriately to get megapixels:

ArrayList<Integer> supportedMegaPixels;
Camera cam = Camera.open();
camParameters = cam.getParameters();
pictureSizes = camParameters.getSupportedPictureSizes();
 
if (pictureSizes.size() > 0) {
    supportedMegaPixels = new ArrayList<Integer>();
    for (int i = 0; i < pictureSizes.size(); i++) {
        supportedMegaPixels.add(((pictureSizes.get(i).height * 
            pictureSizes.get(i).width) + 1000000 / 2) / 1000000);
    }
}
 
cam.release();

You can then check the supportedMegaPixels ArrayList to see the maximum number of megapixels as well as to see if a specific resolution was supported.  For example, here’s a method that will check to see if the camera supports 5 megapixels:

public static boolean doesSupport5MP() {
    return (supportedMegaPixels.contains(5));        
}

I do have a few words of caution.  First, if your device has a front facing camera as well, it will return the supported picture sizes of that camera along with the picture sizes for the (typically) higher quality rear camera.  Secondly, if you’re using the camera and calling Camera.open() make sure you call release on the camera object when you’re done.  If you open the camera and then try to fire the ACTION_IMAGE_CAPTURE intent, the camera won’t work. 

If you need more control over the camera or want to make your own image capture program, you’ll want to directly connect with the camera hardware.  We won’t go over that today but you can read more about it on the Android site (and maybe I’ll go over it in the future).

You can download the code we ended up with today here.


Chris Risner