Wednesday, January 25, 2012

Android API quirks - getting an image from gallery

Here's the recommended intent to get an image from gallery

final Intent i = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, REQUEST_GALLERY);

And then, uri can be retrieved from Intent in onActivityResult:

final Uri imageUri = data.getData();
final String[] columns = { MediaColumns.DATA }
final Cursor cursor = activity.getContentResolver().query(imageUri, columns, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(MediaColumns.DATA);
String filePath = cursor.getString(columnIndex);

However, on never OS's, 3.x and 4.x, images may come from picassa with the following urls: "content://com.google.android.gallery3d.provider/picasa/item/", and then MediaColumns.DATA cannot be used to retrieve file path.

There is an unresolved issue in google issue tracker. I found a way to support picassa urls. Namely, just use the following columns when querying provider (second one is supported by picassa):

final String[] columns = { MediaColumns.DATA, MediaColumns.DISPLAY_NAME };

and then, put it all together to get the actual data (using openInputStream for picassa provider):

int columnIndex = cursor.getColumnIndex(MediaColumns.DATA);
if (columnIndex != -1) {
//regular processing for gallery files
String fileName = cursor.getString(columnIndex);
} else {
// this is not gallery provider
columnIndex = cursor.getColumnIndex(MediaColumns.DISPLAY_NAME);
if (columnIndex != -1) {
final InputStream is = getContentResolver().openInputStream(imageUri);
}
}

I think that API is a bit quirky here, so this is more of a workaround, not real solution. If anyone knows a better solution, please post.

6 comments:

  1. Hi,

    For picasa images, do you get anything for MediaColumns.SIZE?

    ReplyDelete
  2. Yes, MediaColumns.SIZE works for picassa provider

    ReplyDelete
  3. Hey, Im getting errors if the picture has not been previously cached. Do you have any idea how to go around it?

    I get something like this on the log:

    W/PicasaStore(29417): download fail
    W/PicasaStore(29417): android.os.NetworkOnMainThreadException
    W/PicasaStore(29417): at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1084)
    W/PicasaStore(29417): at java.net.InetAddress.lookupHostByName(InetAddress.java:391)
    W/PicasaStore(29417): at java.net.InetAddress.getAllByNameImpl(InetAddress.java:242)
    W/PicasaStore(29417): at java.net.InetAddress.getAllByName(InetAddress.java:220)
    W/PicasaStore(29417): at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:137)
    W/PicasaStore(29417): at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:164)
    W/PicasaStore(29417): at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:119)
    W/PicasaStore(29417): at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:360)
    W/PicasaStore(29417): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:555)
    W/PicasaStore(29417): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:487)
    W/PicasaStore(29417): at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:465)
    W/PicasaStore(29417): at com.google.android.picasasync.HttpUtils.openInputStream(HttpUtils.java:92)
    W/PicasaStore(29417): at com.google.android.picasasync.PicasaDownloadManager.openInputStream(PicasaDownloadManager.java:205)
    W/PicasaStore(29417): at com.google.android.picasasync.PicasaStore.findInCacheOrDownload(PicasaStore.java:438)
    W/PicasaStore(29417): at com.google.android.picasasync.PicasaStore.openContentImage(PicasaStore.java:420)
    W/PicasaStore(29417): at com.google.android.picasasync.PicasaStore.openFile(PicasaStore.java:396)
    W/PicasaStore(29417): at com.google.android.picasasync.PicasaContentProvider.openFile(PicasaContentProvider.java:508)
    W/PicasaStore(29417): at android.content.ContentProvider.openAssetFile(ContentProvider.java:715)
    W/PicasaStore(29417): at android.content.ContentProvider.openTypedAssetFile(ContentProvider.java:823)
    W/PicasaStore(29417): at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:249)
    W/PicasaStore(29417): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:287)
    W/PicasaStore(29417): at android.os.Binder.execTransact(Binder.java:338)
    W/PicasaStore(29417): at dalvik.system.NativeStart.run(Native Method)
    D/MetricsUtils(29417): [OPEN content://com.google.android.apps.plus.content.EsGooglePhotoProvider/photos/

    ReplyDelete
  4. Thx! This way worked for me, but after little refactoring:
    we call activity for result:
    final Intent i = new Intent(
    Intent.ACTION_PICK,
    android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(i, PICTURE_GALLERY); //PICTURE_GALLERY = 1 (constant, declared in our Activity class)

    later I Override onActivityResult and declare method for obtaining Bitmap from File

    @Override
    protected void onActivityResult(int requestCode, int resultCode,
    Intent intent) {
    super.onActivityResult(requestCode, resultCode, intent);
    final Uri imageUri = intent.getData();
    final String[] columns = { MediaColumns.DATA, MediaColumns.DISPLAY_NAME };
    final Cursor cursor = this.getContentResolver().query(imageUri, columns, null, null, null);
    cursor.moveToFirst();
    try {
    final InputStream is = getContentResolver().openInputStream(imageUri);
    File f = new File(this.getCacheDir(), "temporary");
    OutputStream os = new FileOutputStream(f);
    Utils.CopyStream(is, os);
    os.close();
    useObtainedBitmap(decodeFile(f));
    is.close();
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    } catch (IOException e) {
    e.printStackTrace();
    }
    cursor.close();
    }

    private Bitmap decodeFile(File f) {
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Bitmap.Config.ARGB_8888;
    Bitmap outBitmap = null;
    try {
    outBitmap = BitmapFactory.decodeStream(new FileInputStream(f),
    null, options);
    } catch (FileNotFoundException e) {
    e.printStackTrace();
    }
    return outBitmap;
    }

    I'am not pretending for my code clean, but it works for me, and I hope it will help someone else

    ReplyDelete