How to get Picasa images using the Image Picker on Android devices running any OS version

Using the Image Picker to get a local image from the Gallery is pretty easy and trivial task. But things get a lot more interesting if the user has a Picasa account and he/she happens to select an image from one of their Picasa albums. If you do not handle this scenario, your app will crash! And there is no way for you to tell the Image Picker to show just local files. So, you have to handle it, or you will be releasing a buggy application!

Things got even more interesting after the release of Honeycomb! All of a sudden the code that was fetching Picasa images and was working flawlessly started failing on devices running OS 3.0 and up. After some investigation I found the culprit- Google changed the URI returned when the user was selecting a Picasa image. This change was completely undocumented, or at least I could not find any documentation on this! So, on devices running Android OS prior to 3.0 the URI returned was an actual URL and now on devices running OS 3.0 and higher, the URI returned had a different format. For example:

1. https://lh4.googleusercontent.com/… (URI returned on devices running OS prior to 3.0)
2. content://com.google.android.gallery3d (URI returned on devices running OS 3.0 and higher)

I posted a brief solution on the official Android bug report site, but I will expand on it here.

In order to properly handle fetching an image from the Gallery you need to handle three scenarios:

1. The user selected a local image file
2. The user selected a Picasa image and the device is running Android version prior to 3.0
3. The user selected a Picasa image and the device is running Android version 3.0 and higher

Let’s delve straight into the code:

1. Start the Image Picker:


private static final int PICTURE_GALLERY = 1;
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(photoPickerIntent, PICTURE_GALLERY);

In the first line above I am passing a URI to the intent because I would like to fetch the full image, not just get a Bitmap with the thumbnail. The Android team did a good job of acknowledging that if you just wanted a thumbnail of the image then you can get back a Bitmap object containing it, since a thumbnail would not be that big in size. But if you wanted the full image, it would be foolish to do the same, since that would take up a big chunk of the heap! So, what you get instead is some data about the image file, among which is the path to it.

2. We need to implement the onActivityResult method that will be called when the user closes (picks the image) the Image Gallery application:


@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
	super.onActivityResult(requestCode, resultCode, intent);
	switch (requestCode) {
		case PICTURE_GALLERY:
		if (resultCode == RESULT_OK && intent != null) {
			Uri selectedImage = intent.getData();
			final String[] filePathColumn = { MediaColumns.DATA, MediaColumns.DISPLAY_NAME };
			Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
			// some devices (OS versions return an URI of com.android instead of com.google.android
			if (selectedImage.toString().startsWith("content://com.android.gallery3d.provider"))  {
				// use the com.google provider, not the com.android provider.
				selectedImage = Uri.parse(selectedImage.toString().replace("com.android.gallery3d","com.google.android.gallery3d"));
			}
			if (cursor != null) {
				cursor.moveToFirst();
				int columnIndex = cursor.getColumnIndex(MediaColumns.DATA);
				// if it is a picasa image on newer devices with OS 3.0 and up
				if (selectedImage.toString().startsWith("content://com.google.android.gallery3d")){
					columnIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
					if (columnIndex != -1) {
						progress_bar.setVisibility(View.VISIBLE);
						final Uri uriurl = selectedImage;
						// Do this in a background thread, since we are fetching a large image from the web
						new Thread(new Runnable() {
							public void run() {
								Bitmap the_image = getBitmap("image_file_name.jpg", uriurl);
							}
						}).start();
					}
				} else { // it is a regular local image file
					String filePath = cursor.getString(columnIndex);
					cursor.close();
					Bitmap the_image = decodeFile(new File(filePath));
				}
			}
			// If it is a picasa image on devices running OS prior to 3.0
			else if (selectedImage != null && selectedImage.toString().length() > 0) {
				progress_bar.setVisibility(View.VISIBLE);
				final Uri uriurl = selectedImage;
				// Do this in a background thread, since we are fetching a large image from the web
				new Thread(new Runnable() {
					public void run() {
						Bitmap the_image = getBitmap("image_file_name.jpg", uriurl);
					}
				}).start();
			}
		}
		break;
	}
}

3. Implement the getBitmap method, which will download the image from the web if the user selected a Picasa image:


private Bitmap getBitmap(String tag, Uri url) 
{
	File cacheDir;
	// if the device has an SD card
	if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
		cacheDir=new File(android.os.Environment.getExternalStorageDirectory(),".OCFL311");
	} else {
		// it does not have an SD card
	   	cacheDir=ActivityPicture.this.getCacheDir();
	}
	if(!cacheDir.exists())
	    	cacheDir.mkdirs();

	File f=new File(cacheDir, tag);

	try {
		Bitmap bitmap=null;
		InputStream is = null;
		if (url.toString().startsWith("content://com.google.android.gallery3d")) {
			is=getContentResolver().openInputStream(url);
		} else {
			is=new URL(url.toString()).openStream();
		}
		OutputStream os = new FileOutputStream(f);
		Utils.CopyStream(is, os);
		os.close();
		return decodeFile(f);
	} catch (Exception ex) {
		Log.d(Utils.DEBUG_TAG, "Exception: " + ex.getMessage());
		// something went wrong
		ex.printStackTrace();
		return null;
	}
}

The decodeFile method takes a reference to a file and returns a Bitmap. This is pretty trivial. In my case I also scale down the image as I am reading it from the file, so it does not take much memory.

I think Google has some work to do about all this and if they want to seamlessly integrate Picasa with the Android Gallery application, they should have done a better job with the Image Picker functionality. The developer should not be doing all this heavy lifting and be concerned with where the actual image resides. The returned URI should be exactly the same no matter if the image is local or not. And if it is not local, the Gallery app should be fetching it for us. That way, we will have a consistent and predictable functionality. Right now you will be surprised how many applications out there do not handle Picasa images and crash with an ugly error.

Thursday, March 1st, 2012 Android, Eclipse

17 Comments to How to get Picasa images using the Image Picker on Android devices running any OS version

  1. Hi Dimitar Darazhansk!
    I use link com.android.sec.gallery3d to get image from picasa.
    But maybe it got image not from picasa web. Image after decode was broken. (like thumbnail)

    Can you help me! Thank alot!

  2. Bang on March 25th, 2012
  3. Hi Dimitar,

    Thanks for that neat solution. I struggled to find a fix for that weird Picasa loader. I’ve implemented yours and it works fine.

    For anyone implementing that:

    - Utils.CopyStream(is, os) is not provided and can be replaced by:
    byte[] buffer = new byte[1024];
    int len;
    while ((len = is.read(buffer)) != -1) {
    os.write(buffer, 0, len);
    }

    - I also get URI starting with “content://com.android.sec.gallery3d” instead of “content://com.google.android.gallery3d”. So I added a second string comparison test to handle that URI

  4. Julien on June 10th, 2012
  5. Thanks for the post, but wouldn’t be nice if you can include all the files as download. Thank you.

  6. Nizzy on August 19th, 2012
  7. I create a local file with uri i keep track of and set it like so. It works with picasa and is a lot cleaner

    cameraIntent.putExtra( android.provider.MediaStore.EXTRA_OUTPUT, outuri);

  8. Maurice on October 1st, 2012
  9. Thanks very much for writing this post. It pretty much solved my Picasa problem.

    I did find that, in your getBitmap() method, getContentResolver().openInputStream(url) kept returning an empty stream on my Galaxy SIII test phone. It was a non-null stream, just with no data. I spent some time Googling around, and I found two possible solutions:

    Bitmap bitmap = android.provider.MediaStore.Images.Media.getBitmap(getContentResolver(), url);
    // Returns the image directly as a bitmap, no need for extra decode step

    or

    BitmapFactory.Options options = new BitmapFactory.Options();
    android.content.res.AssetFileDescriptor fd = getContentResolver().openAssetFileDescriptor(url, “r”);
    Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, options);
    fd.close();
    // Get at Bitmap decoding of the image directly in case you need to play will options (like reducing the file size)

    Hopefully this will save some people from headaches in the future. Thanks again!

  10. Joel on January 7th, 2013
  11. Joel thanks for sharing that solution, it works =)… just to clarify on this case there is no need to replace “content://com.sec.android.gallery3d” for “content://com.google.android.gallery3d”

  12. Agnes on January 9th, 2013
  13. Hi,

    thanks a lot, this will help me to fix a bug. Are you aware of a library that handles the “load image from intent” stuff? Because there are other specialities to handle, if you select an image from DropBox for example.

    If not, do you mind, if I include your code in such a library and open source it?

    Andreas

  14. Andreas Ebbert-Karroum on April 5th, 2013
  15. @Andreas,

    Sorry for the delayed response… but yes.. go ahead an include the code anywhere you think it might be useful!

  16. dimitar on April 9th, 2013
  17. Thank’s a lot,

    my problem was fixed. That can filter the URI from DropBox to.
    I just filter the URI for android.gallery3d and that’s running well till now.

  18. Hengki on April 9th, 2013
  19. Thank you for sharing your info. I truly appreciate your efforts and I
    will be waiting for your further post thanks once again.

  20. Dominik on April 11th, 2013
  21. Truly when someone doesn’t be aware of then its up to other people that they will help, so here it occurs.

  22. get more Info on May 12th, 2013
  23. POST A ZIP FOR GODS SAKE….what is wrong with ppl?

  24. Chuck on August 30th, 2013
  25. This is just what I needed. Thankyou for this.

  26. Evans Kakpovi on August 31st, 2013
  27. Hi,
    Thank you very much for sharing such type of great thought.
    I can’t see Picasa/Flickr options on my devices . I see options of Sky-drive and Google drive but I unable to access images into my apps from Sky-drive/ Google Drive .
    Could you please help me ?
    Thank you very much.

    Regards,
    Maidul Islam

  28. Md Maidul Islam on January 31st, 2014
  29. So, how about videos?

  30. desgraci on March 13th, 2014
  31. Generally I do not learn post on blogs, however
    I wish to say that this write-up very forced me to try and do it!
    Your writing style has been surprised me. Thanks, quite great post.

  32. Nickolas on March 17th, 2014
  33. Maybe I’m dense: what provide Utils.* and what provides ActivityPicture?

  34. Matthew Briggs on July 2nd, 2014

Leave a comment

*

Search

 

Archive

August 2014
M T W T F S S
« Jul    
 123
45678910
11121314151617
18192021222324
25262728293031

Other