Tutorial:Camera/Gallery Part II

From Notes

Jump to: navigation, search

This tutorial describes a way to associate just certain images with the Gallery View. Recall from the Tutorial:Camera_and_Gallery_Demo that the Gallery in that example displayed all of the images stored on the phone. In this example, we populate the Gallery with just those images created by the Demo App.

Contents

Background

Camera images are saved in Android's MediaStore.Images.Media.EXTERNAL_CONTENT_URI and the thumbnails are saved in MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI. The thumbnails record contains a reference to the corresponding image in their IMAGE_ID field.

Therefore, given the thumbnail, you can retrieve the corresponding image as is most likely done by the built-in Camera and Gallery applications. However, the image's record does not contain a usable reference to the corresponding thumbnails. There is a field named MINI_THUMB_MAGIC in the image record. But this field is apparently overwritten by Android's built-in Gallery or Slideshow App. Therefore, we use the image's PICASA_ID field to store the ID of the image's corresponding mini thumbnail.

Algorithm

The ACTION_IMAGE_CAPTURE activity returns a bitmap to the onActivityResult() method.

  1. Save the captured bitmap using MediaStore.Images.Media.insertImage(getContentResolver(), bm, OUR_TITLE, TAG). This associates OUR_TITLE with the image.
  2. Use the image's ID to retrieve the corresponding mini thumbnail record from MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI.
  3. Use a ContentResolver to set the image's PICASA_ID field to the ID number of the Thumbnail.

In the displayGallery() method, we retrieve only those images taken by our App.

  1. Query MediaStore.Images.Media for images with the title = OUR_TITLE.
  2. Pass the resulting Cursor to the ImageAdapter
  3. Associate the ImageAdapter to the GalleryView

In the ImageAdapter.getView() method:

  1. Retrieve the image's corresponding mini Thumbnail Uri
  2. Set the ImageView in the Gallery that the Thumbnail Uri

In the onClickItemListener() method:

  1. Retrieve the image using the ID stored in the Cursor

Implementation: onActivityResult()

To implement this modification, we need a new constant defintion:

 private static final String OUR_TITLE = "DemoImage";

and we need to replace the call to insertImage() in the onActivityResult() method with a call to a new method, saveImageAndThumbnails(Bitmap):

// Revised code in onActivityResult()

if (b.containsKey(MediaStore.EXTRA_OUTPUT)) { // large image?
	showToast(this,"Large image");
	// Should have to do nothing for big images -- should already saved in MediaStore ... but
	saveImageAndThumbnails(bm);
	Log.i(TAG, "This is a large image: ");
} else {
	showToast(this,"Small image");
	saveImageAndThumbnails(bm);
	Log.i(TAG, "This is a small image: ");
}
break;

The saveImageAndThumbnails() method, implements the first part of the above algorithm:

private void saveImageAndThumbnails(Bitmap bm) {
	// Save the image.  This also saves a micro and mini thumbnail
	String sUri = MediaStore.Images.Media.insertImage(getContentResolver(), bm, OUR_TITLE, TAG);
		
	// Now update the mini Thumbnail record with the image's ID
	int imgId = Integer.parseInt(sUri.substring(sUri.lastIndexOf("/")+1)); // Strip off the Id num
	Uri thumbUri = MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI;      // Thumbnail provider
		
	// Query for the mini thumbnail for the image just saved. There should be just 1.
	// See the comments on showGallery() for an explanation of managedQuery()
	String[] projection = {
			MediaStore.Images.ImageColumns._ID,  // The columns we want
			MediaStore.Images.Thumbnails.IMAGE_ID,  
			MediaStore.Images.Thumbnails.KIND };
	String selection = 
		MediaStore.Images.Thumbnails.KIND + " = "  +  MediaStore.Images.Thumbnails.MINI_KIND  
		+ " AND " + MediaStore.Images.Thumbnails.IMAGE_ID + " = " + imgId;
	
	Cursor c = this.managedQuery(thumbUri, projection, selection, null, null); // Should return just 1
	c.moveToFirst();
	Log.i(TAG, "Save Cursor = " + c.getCount());
	int thumbId = c.getInt(c.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID)); // Thumbnail ID
			
	// Update the image's entry with the Thumbnail's ID
	ContentResolver cr = getContentResolver();
	ContentValues values = new ContentValues(); 
	values.put(MediaStore.Images.ImageColumns.PICASA_ID, thumbId);
	cr.update(Uri.parse(sUri), values, "", null);		
}

Implementation: displayGallery()

In the previous version of displayGallery() we queried for mini thumbnails and passed them along to ImageAdapter. This time we query for the images themselves and pass them along to ImageAdapter:

private void displayGallery() {
	Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; // Where images are stored
	displaySdCard();

	String[] projection = {                     // The columns we want
			MediaStore.Images.ImageColumns._ID,  
			MediaStore.Images.ImageColumns.TITLE,
			MediaStore.Images.ImageColumns.PICASA_ID }; // PICASA_ID is thumbnail's ID

	String selection = MediaStore.Images.ImageColumns.TITLE + " = \"" + OUR_TITLE + "\""; // Our images

	mCursor = this.managedQuery(uri, projection, selection, null, null);
		
	// Create an ImageAdapter with the Cursor and assign it to the Gallery
	if (mCursor != null) { 
		mCursor.moveToFirst();
		ImageAdapter adapter = new ImageAdapter(mCursor, this);
		Log.i(TAG, "displayGallery(), adapter = " + adapter.getCount());
		mGallery.setAdapter(adapter);
		mGallery.setOnItemClickListener(this);
	} else 
		showToast(this, "Gallery is empty.");
}

Implementation: ImageAdapter.getView()

In the previous version of getView() the Adapter was passed thumbnails, which were assigned to the appropriate ImageView in the gallery. In this version, we are passed images. We retrieve the associated thumbnail ID from the PICASA_ID field, retrieve the thumbnail's Uri, and set the ImageView:

public View getView(int position, View convertView, ViewGroup parent) {
    Log.i(TAG, "Get view = " + position);
    ImageView i = new ImageView(mContext);
    mCursor.requery();
    	  	
    if (convertView == null) {
    	mCursor.moveToPosition(position);

    	// Protocol: PICASA_ID is set to _ID of the mini thumbnail when the image is saved.
    	int thumbId = mCursor.getInt(mCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns.PICASA_ID));
    	Uri thumbUri = Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, ""+thumbId);
   		 
    	Log.i(TAG, "Thumbnail Uri = " + thumbUri.toString());
    	try {
    	    i.setImageURI(thumbUri);
    	    i.setScaleType(ImageView.ScaleType.FIT_XY);
    	    i.setLayoutParams(new Gallery.LayoutParams(136, 136));
    	    i.setBackgroundResource(mGalleryItemBackground);
    	} catch (Exception e) {
    	    Log.i(TAG, "Exception " + e.getStackTrace());
    	}
    }  
    return i;
}

Implementation: onClickItemListener()

In the previous version we were passing thumbnails to the Cursor and had to retrieve the corresponding image, using the IMAGE_ID field in the Cursor. Now we are passing images to the Cursor, so we retrieve the image's ID from the Cursor:

    mCursor.moveToPosition(position);
//long id = mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails.IMAGE_ID));
    long id = mCursor.getLong(mCursor.getColumnIndexOrThrow(MediaStore.Images.ImageColumns._ID));

Running the App

The source code is here: Media:camera-gallery2.zip. Download the zip file. Unzip it and then use it to create a new Android project.

References

Challenges

  1. Explore the documentation for MediaStore.Images and its subclasses and develop code to store other information with the images.
Personal tools
NSF K-12