Linux.com

Home Learn Linux Linux Tutorials How to Call the Camera in Android Apps Part 2: Capture and Store Photos

How to Call the Camera in Android Apps Part 2: Capture and Store Photos

In the last Android tutorial, we started building a basic Camera activity on Android. This will allow us to control and operate the device camera directly, rather than just firing off an Intent to the default camera activity. The code in the last tutorial set up the camera and the camera preview. Now we're going to capture and store a photo.

Setup

We already set up the manifest to allow the use of the camera. To store a photo, we also have to add permission to use the external storage, by editing AndroidManifest.xml:

android camera saved image

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

Next, we need to add a 'take photo' button to the camera preview. Edit main.xml to include a button:

<LinearLayout ... >
  <FrameLayout ... />
  <Button
      android:id="@+id/button_photo"
      android:text="Take Photo"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:layout_gravity="center"
      />
</LinearLayout>

(It's preferable to use strings.xml to store the button text rather than hard-coding it as it is here.)

Capturing and storing a photo

If you run the code now, you should see a Take Photo button; but as yet it doesn't do anything. We need to write the code that handles it. Add this to setUpLayout() in MyCameraActivity:

Button captureBtn = (Button) findViewById(R.id.button_photo); 
captureBtn.setOnClickListener(
  new View.OnClickListener() {
    public void onClick(View v) {
      takePhoto();
    }
  }
);

takePhoto() does the work of grabbing an image from the camera preview:

protected static final int MEDIA_TYPE_IMAGE = 0; 
private void takePhoto() {
  PictureCallback pictureCB = new PictureCallback() {
    public void onPictureTaken(byte[] data, Camera cam) {
      File picFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
      if (picFile == null) {
        Log.e(TAG, "Couldn't create media file; check storage permissions?");
        return;
      }
  
      try {
        FileOutputStream fos = new FileOutputStream(picFile);
        fos.write(data);
        fos.close();
      } catch (FileNotFoundException e) {
        Log.e(TAG, "File not found: " + e.getMessage());
        e.getStackTrace();
      } catch (IOException e) {
        Log.e(TAG, "I/O error writing file: " + e.getMessage());
        e.getStackTrace();
      }
    }
  };
  camera.takePicture(null, null, pictureCB);
}

Let's look at that in more detail. Android provides the PictureCallback interface to get a hold of the image data from a camera. As you can see, most of the code in takePhoto() sets up the PictureCallback. The final line is the one that hooks this new callback into the camera by passing it into takePicture() from the Camera API. So when the user hits the Capture button, the method uses takePicture() to tell the camera to take a photo, then passes the data into the PictureCallback.

In the PictureCallback, we get a file to write to, write to it, and deal with the various possible errors. (If you were writing a different sort of app, you might do something entirely different with this photo data, such as displaying it or storing it in a database.) To get a specific output file, we need a helper method:

private File getOutputMediaFile(int type) {
  File dir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_PICTURES), getPackageName());
  if (!dir.exists()) {
    if (!dir.mkdirs()) {
      Log.e(TAG, "Failed to create storage directory.");
      return null;
    }
  }
  String timeStamp = 
      new SimpleDateFormat("yyyMMdd_HHmmss", Locale.UK).format(new Date());
  if (type == MEDIA_TYPE_IMAGE) {
    return new File(directory.getPath() + File.separator + "IMG_"  
                    + timeStamp + ".jpg");
  } else {
    return null;
  }
}

There are two options for getting a storage directory on the SD card:

  1. Environment.getExternalStoragePublicDirectory(): this uses a public directory. If the app is uninstalled, the photos will remain.
  2. Context.getExternalFilesDir(): this uses a directory which is private to the app. If the app is uninstalled, the photos will also be deleted.

When deciding which of these to use, you should think carefully about the behavior your users will expect if, or when, they delete your app. In most cases, users might be quite disappointed to find photos unexpectedly deleted, but this will depend on the details of your app.

If using the public directory, as here, make sure you create a subdirectory for storage (as here, one possible directory name is whatever is returned by getPackageName()). Otherwise your user winds up with a lot of photos polluting their root external storage. Once the directory is chosen -- and if necessary, created -- we also create a filename; here using date/time, as is fairly standard. Here we're only dealing with JPG media files. If you wish to handle video as well, you can add code to deal with that and save it appropriately. However, note that video is a lot more complicated to manage.

Background code

Run the code and try it out. You can now save a file (as you'll see if you use a file manager app to navigate to the storage directory).

However, you'll find that the preview freezes as you take the photo. You can restart it easily, by adding this line to takePhoto():

camera.takePicture(null, null, pictureCB);
camera.startPreview();

Even with this line, though, the preview freezes while the file is being stored. This is because as our code stands, everything is being done on the same thread. We looked at threading in the Canvas tutorial, but instead of writing your own thread, AsyncTask provides a quick way of handing something out to a background helper thread with minimal code. (Remember, the Android code isn't threadsafe: only use a background thread for something that doesn't involve the UI. Here, we're only saving the photo in the background.) We'll look at AsyncTask in detail in another tutorial, but here's the code to fix the problem:

private void takePhoto() {
  PictureCallback pictureCB = new PictureCallback() {
  	public void onPictureTaken(byte[] data, Camera cam) {
   	  new SavePhotoTask().execute(data);
      cam.startPreview();
	}
  };
  camera.takePicture(null, null, pictureCB);
}
class SavePhotoTask extends AsyncTask<byte[], String, String> {
  @Override
  protected String doInBackground(byte[]... data) {
    File picFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
    if (picFile == null) {
	  Log.e(TAG, "Error creating media file; are storage permissions correct?");
	  return null;
    }    
  android camera capture  byte[0] photoData = data[0];
    try {
        FileOutputStream fos = new FileOutputStream(picFile);
        fos.write(photoData);
        fos.close();
      } catch (FileNotFoundException e) {
        Log.e(TAG, "File not found: " + e.getMessage());
        e.getStackTrace();
      } catch (IOException e) {
        Log.e(TAG, "I/O error with file: " + e.getMessage());
        e.getStackTrace();
      }
    return null;
  }
}

Note that you need to get the single array photoData out of the multi-dimensional array data before you can write it to the file. Otherwise, it's the same code just moved into a separate class and thread. Run it, then check out your saved photo with your file manager.  

There are a lot of improvements you can make to this app; cameras have lots of different capabilities, for example. In further tutorials we'll look at taking video and at adding options for flash and other camera capabilities.

 

Comments

Subscribe to Comments Feed
  • Dan Said:

    Can you send me the source code for this tutorial...I'm having difficulty with seeing the whole thing together.

  • babu Said:

    I'm new to android development. Can you send me the source code for this tutorial.. I wanna try it out.

  • madhu Said:

    hello, how to save captured pics into one floder in gallery with app name, can you please send me code for that. Thanks in advance

  • J. Said:

    Hey there, I am pretty new to Android Programming and am attempting to do something similar to what you have done. Would it be possible to get a copy of your source code for my studies? Thanks.

  • Maqsood Said:

    Hi! I am trying to make a camera app that will take the picture all by itself. Can you please guide me on this. Also kindly send me the entire source code of this tutorial. Thanks

  • hi master Said:

    sent to me the source code... fadz1606@gmail.com ...

  • Herrry Said:

    I just want to mention I am just beginning to weblog and truly enjoyed your web blog. Likely I’m planning to bookmark your site. You certainly come with awesome writings. Regards for revealing your website page. updatemyandroid

  • Sani Elfishawy Said:

    I think you may have a typo in the line: return new File(directory.getPath() + File.separator + "IMG_" the variable you set previously is dir not directory.

  • David Barker Said:

    Great tutorial! I am learning to apply Java programming to an android device because I think it will be a fun way to learn both and improve my skills. This tutorial is definetely a great first step for me towards my goal! The app I'm looking to make will help me in my daily activities too. I have a question regarding the setUpLayout() from the previous camera tutuorial, I can't seem to find that function in the code snippets you presented. Can you "fill me in" on this one or point me to the code? It would also be great if you can share the whole project source code as it would help me understand everything together (might also give me more questions to ask though.. :-) ) Regards, David

  • David Barker Said:

    Great tutorial! I am learning to apply Java programming to an android device because I think it will be a fun way to learn both and improve my skills. This tutorial is definetely a great first step for me towards my goal! The app I'm looking to make will help me in my daily activities too. I have a question regarding the setUpLayout() from the previous camera tutuorial, I can't seem to find that function in the code snippets you presented. Can you "fill me in" on this one or point me to the code? It would also be great if you can share the whole project source code as it would help me understand everything together (might also give me more questions to ask though.. :-) ) Regards, David

  • nida Said:

    can someone pls help me how to show live camera in more than one framelayouts (container for camera) in android its working fine for one framelayout i.e framelayout.addView(cameraInstance); but framelayout2.addView(cameraInstance); // the second line is giving me error


Who we are ?

The Linux Foundation is a non-profit consortium dedicated to the growth of Linux.

More About the foundation...

Frequent Questions

Join / Linux Training / Board