Capturing, Saving, and Displaying an Image in Android (1.5, 1.6, 2.0, 2.1, 2.2, Sense UI - Hero)

The process for capturing, saving and displaying an image in Android can be quite annoying, especially if you want it to work across all devices.

With the release of the Sense UI (an HTC creation), it's made things 1 step more difficult. So, the reason I'm writing this article is because I've been stuck on this same issue for quite awhile, and there isn't enough information out there to let us know why it simply won't work.

Simply put, the Sense UI handles things differently when it comes to saving a new image after an intent was called (ie. new Intent(MediaStore.ACTION_IMAGE_CAPTURE)). In this article I'll show you how I overcame this problem and walk you through my code.

Below are a few snipplets of code that I'll explain.


Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
file = new File(Environment.getExternalStorageDirectory(), ReceiptData.PICTURE_PATH + String.valueOf(System.currentTimeMillis()) + ".jpg");

outputFileUri = Uri.fromFile(file);
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);

startActivityForResult(intent, 1);

Here we're creating the intent, which will open up the camera window, which in turn allows you to take a picture.

We also set a new file, where the newly taken picture will be saved.

The next step is to create a Uri object that points to the newly created file (which we'll use in the next step).

One of the more important steps is the intent.putExtra method, which will allow us to specify where we want the image to be saved - if you don't include this, it'll just return a thumbnail of the image, and store it in a temporary/default location. So, we just give it the Uri object, and it'll know where it needs to save.

And lastly, we start the camera activity and await a result (the image).


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == 1) {
    switch( resultCode ) {
      case 0:
        break;

      case -1:
                  
        mIv.setImageBitmap(mi.setupImage(data));
        break;
    }
  }
}

In the next bit of code we're awaiting the result from the camera, and once we receive it, we are going to pass the data into a method called setupImage, which I'll be explaining next. This will be returning a bitmap object, which you then use in the ImageView.


public Bitmap setupImage(Intent data) {
  BitmapFactory.Options options = new BitmapFactory.Options();
  options.inSampleSize = SAMPLE_SIZE;     // SAMPLE_SIZE = 2

  Bitmap tempBitmap = null;
  Bitmap bm = null;
  try {
    tempBitmap = (Bitmap) data.getExtras().get("data");
    bm = tempBitmap;

    Log.v("ManageImage-hero", "the data.getData seems to be valid");
    
    FileOutputStream out = new FileOutputStream(outputFileUri.getPath());
    tempBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
  } catch (NullPointerException ex) {
    Log.v("ManageImage-other", "another phone type");
    bm = otherImageProcessing(options);
  } catch(Exception e) {
    Log.e("ManageImage-setupImage", "problem setting up the image", e);
  }

  return bm;
}

This piece is the most important for distinguishing whether it's an Android device using the HTC Sense UI or something else. If it's using the Sense UI you'll be able to retrieve the picture by calling (Bitmap) data.getExtras().get("data"), or if it's another device, then data will be null and that will in-turn throw a NullPointerException (hence why we're catching it up top). So, if it's some other device, we catch the fact that it's null, and call a separate method for processing the image (otherImageProcessing(options), which I'll talk about after this.

The first thing we do here is we take what's in the data Intent and put it into a tempBitmap object, since it's a bitmap that is being returned (assuming this is a Sense UI device - ie. HTC Hero, Legend, etc..). We will then pass the tempBitmap into bm, which we will be using to return back to the ImageView, and the tempBitmap we'll be compressing as a JPEG and saving it to the correct location.

If you're confused about the location bit, let me explain. For the Sense UI devices, it doesn't save the image in the location that you tell it to when using the Intent.putExtra method - instead it saves it into a temporary location, which is why we need to use the Intent data object to retrieve it. This is why we need to save it to a different location, so that we may access it again at later dates whenever we need it.

And finally we return the Bitmap.


private Bitmap otherImageProcessing(BitmapFactory.Options options) {
  Bitmap bm = null;

  try {
    FileInputStream fis = new FileInputStream(outputFileUri.getPath());
    BufferedInputStream bis = new BufferedInputStream(fis);
    bm = BitmapFactory.decodeStream(bis, null, options);

    // cleaning up
    fis.close();
    bis.close();
  } catch(Exception e) {
    Log.e("ManageImage-otherImageProcessing", "Cannot load image", e);
  }

  return bm;
}

Here we process the image properly, based on the path we had originally selected for it to be saved to.

So we load the Bitmap and return it - not going to explain this further because it's pretty self explanatory.

The biggest issue here was understanding the difference between the Android HTC Sense UI and the regular UI - the Sense UI has it's own way of managing captured images, unfortunately, so we have to accommodate it.

This took me a really long time to figure out, mainly because I don't have a Sense UI device.

Anyways, I hope this article helps other people out there experiencing the same issues.

jon | May 15, 2010 | Comments (6)

Comments

I'm quite pelased with the information in this one. TY!
Comment by Carlinda - January 28, 2012 @ 6:05 pm
That’s not just logic. That’s rellay sensible.
Comment by Kiona - May 07, 2011 @ 1:12 am
Sure would be nice if you put an email on your website or respond to twitter comments.

Your example above is nice but is incorrect, unless you expect everyone with an HTC type phone to ONLY use thumbnail sized images.
To fix the issue properly you have to scan the media after the intent has completed.
Comment by pr0cs - January 20, 2011 @ 3:50 pm
Great article but I'm sort of stuck. I don't have an HTC device but I want to support images in my app from their device.
Your code works but unfortunately the bitmap saved is only a thumbnail image. Is there any way to cross reference (from the intent data returned perhaps?) where the REAL camera image was saved so I could use/load it?
Comment by pr0cs - January 19, 2011 @ 5:07 pm
What if we could move the image to the desired location, after theHTC UI camera app captures it?
Comment by Michalis Giannakidis - July 25, 2010 @ 3:44 pm
This was very helpful!

Thanks a lot!
Comment by geo - May 15, 2010 @ 4:54 pm

Name (required)
Email (will not be published) (required)
Website

captcha