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.
11 Comments to How to get Picasa images using the Image Picker on Android devices running any OS version
Leave a comment
Search
Archive
| M | T | W | T | F | S | S |
|---|---|---|---|---|---|---|
| « Apr | ||||||
| 1 | 2 | 3 | 4 | 5 | ||
| 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 13 | 14 | 15 | 16 | 17 | 18 | 19 |
| 20 | 21 | 22 | 23 | 24 | 25 | 26 |
| 27 | 28 | 29 | 30 | 31 | ||
Recent Comments
- Olivier on Dynamic Port Forwarding with SOCKS over SSH
- Ld7 on How to connect your Android phone to Ubuntu to do development, testing, installations or tethering
- get more Info on How to get Picasa images using the Image Picker on Android devices running any OS version
- Casper on How to detect a user pan/touch/drag on Android Map v2
- Install SSH as socks proxy for dynamic port forwarding | Steve Constine on Dynamic Port Forwarding with SOCKS over SSH
Categories
Blogroll
Online Tools
Other
BLOG ARCHIVE
- April 2013 (1)
- November 2012 (2)
- August 2012 (1)
- May 2012 (1)
- March 2012 (1)
- November 2011 (1)
- August 2011 (1)
- April 2011 (1)
- January 2011 (2)
- September 2010 (1)
- August 2010 (2)
- July 2010 (2)
- June 2010 (2)
- May 2010 (1)
- January 2010 (2)
- December 2009 (2)
- November 2009 (3)
- October 2009 (1)
- September 2009 (3)
- July 2009 (1)
- May 2009 (1)
- March 2009 (1)
- February 2009 (2)
- January 2009 (2)
- December 2008 (1)
- November 2008 (4)
- October 2008 (5)
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!
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
Thanks for the post, but wouldn’t be nice if you can include all the files as download. Thank you.
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);
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!
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”
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
@Andreas,
Sorry for the delayed response… but yes.. go ahead an include the code anywhere you think it might be useful!
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.
Thank you for sharing your info. I truly appreciate your efforts and I
will be waiting for your further post thanks once again.
Truly when someone doesn’t be aware of then its up to other people that they will help, so here it occurs.