Getting an image from Gallery on from the Picasa//Google + synced folders doesn't work

Solution 1:

I wasted now lots of hours and now i found a solution which works in all cases without any magic downloading in special threads or something. The following method returns a stream from the content which the user selected and this works with everything in the wild.

FileInputStream getSourceStream(Uri u) throws FileNotFoundException {
    FileInputStream out = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        ParcelFileDescriptor parcelFileDescriptor =
                mContext.getContentResolver().openFileDescriptor(u, "r");
        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        out = new FileInputStream(fileDescriptor);
    } else {
        out = (FileInputStream) mContext.getContentResolver().openInputStream(u);
    }
    return out;
}

Solution 2:

After a lot of research, I felt that there were too many workarounds to do something which seems to be quite simple. So, I went ahead a wrote a small library that handles all the complex if elses and happens to work for all possible scenarios that I can think of. Do give it a try and see if this helps.

http://techdroid.kbeanie.com/2013/03/easy-image-chooser-library-for-android.html

This is an initial implementation. Although it handles almost every situation, there could be a few bugs. If you use this library, please let me know your feedback and if at all it could be improved any further.

Solution 3:

I faced the same problem about year ago.. I show you my solution (code is quite messy, please refactor it). So, you have the image URI which was returned from gallery:

    ImageInfo getImage(URI imageUri) {

        ImageInfo result = null;

        final String[] cursorColumns = {MediaStore.Images.Media.DATA, MediaStore.Images.Media.ORIENTATION};

        // some devices (OS versions return an URI of com.android instead of com.google.android
        if (imageUri.toString().startsWith("content://com.android.gallery3d.provider"))  {
            // use the com.google provider, not the com.android provider.
            imageUri = Uri.parse(imageUri.toString().replace("com.android.gallery3d","com.google.android.gallery3d"));
        }

        Cursor cursor = App.getContext().getContentResolver().query(imageUri, cursorColumns, null, null, null);
        if (cursor != null) {

            int dataColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
            int orientationColumnIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.ORIENTATION);

            cursor.moveToFirst();

            // if it is a picasa image on newer devices with OS 3.0 and up
            if (imageUri.toString().startsWith("content://com.google.android.gallery3d")) {

                result = new ImageInfo(downloadImage(imageUri), "0");                       

            } else { // it is a regular local image file

                result = new ImageInfo(cursor.getString(dataColumnIndex), cursor.getString(orientationColumnIndex));

            }

            cursor.close();

        } else {
            result = new ImageInfo(downloadImage(imageUri), "0");
        }

        return result;                      
}

And now we need function to download image:

private String downloadImage(URI imageUri) {

    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 = App.getContext().getCacheDir();
    }

    if(!cacheDir.exists()) cacheDir.mkdirs();
    File f = new File(cacheDir, PUT_HERE_FILE_NAME_TO_STORE_IMAGE);

    try {

        InputStream is = null;
        if (imageUri.toString().startsWith("content://com.google.android.gallery3d")) {
            is = App.getContext().getContentResolver().openInputStream(imageUri);
        } else {
            is = new URL(imageUri.toString()).openStream();
        }

        OutputStream os = new FileOutputStream(f);
        Utils.InputToOutputStream(is, os);

        return f.getAbsolutePath();
    } catch (Exception ex) {
        Log.d(this.getClass().getName(), "Exception: " + ex.getMessage());
        // something went wrong
        ex.printStackTrace();
        return null;
    }
}

ImageInfo is my class to store path to image and its orientation.

public static class ImageInfo {
    public final String filePath;
    public final String imageOrientation;

    public ImageInfo(String filePath, String imageOrientation) {
        this.filePath = filePath;

        if (imageOrientation == null) imageOrientation = "0";
        this.imageOrientation = imageOrientation;           
    }
}

Solution 4:

Based on @drindt answer, below codes give downloaded temporary File path from Google Photo cloud-synced-but-not-in-device file.

@SuppressLint("NewApi")
public static String getFilePath(final Context context, final Uri uri) {

    // Google photo uri example
    // content://com.google.android.apps.photos.contentprovider/0/1/mediakey%3A%2FAF1QipMObgoK_wDY66gu0QkMAi/ORIGINAL/NONE/114919

    if ("content".equalsIgnoreCase(uri.getScheme())) {
        String result = getDataColumn(context, uri, null, null); // 
        if (TextUtils.isEmpty(result))
            if (uri.getAuthority().contains("com.google.android")) {
                try {
                    File localFile = createImageFile(context, null);
                    FileInputStream remoteFile = getSourceStream(context, uri);
                    if(copyToFile(remoteFile, localFile))
                        result = localFile.getAbsolutePath();
                    remoteFile.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        return result;
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;
}

/**
 * Get the value of the data column for this Uri. This is useful for
 * MediaStore Uris, and other file-based ContentProviders.
 *
 * @param context       The context.
 * @param uri           The Uri to query.
 * @param selection     (Optional) Filter used in the query.
 * @param selectionArgs (Optional) Selection arguments used in the query.
 * @return The value of the _data column, which is typically a file path.
 */
static String getDataColumn(Context context, Uri uri, String selection,
                            String[] selectionArgs) {

    Cursor cursor = null;
    final String column = "_data";
    final String[] projection = {
            column
    };

    try {
        cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
                null);
        if (cursor != null && cursor.moveToFirst()) {
            final int column_index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }

    return null;
}


/**
 * Copy data from a source stream to destFile.
 * Return true if succeed, return false if failed.
 */
private static boolean copyToFile(InputStream inputStream, File destFile) {
    if (inputStream == null || destFile == null) return false;
    try {
        OutputStream out = new FileOutputStream(destFile);
        try {
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) >= 0) {
                out.write(buffer, 0, bytesRead);
            }
        } finally {
            out.close();
        }
        return true;
    } catch (IOException e) {
        return false;
    }
}

public static String getTimestamp() {
    try {
        return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ROOT).format(new Date());
    } catch (RuntimeException e) {
        return new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
    }
}

public static File createImageFile(Context context, String imageFileName) throws IOException {
    if (TextUtils.isEmpty(imageFileName))
        imageFileName = getTimestamp(); // make random filename if you want.

    final File root;
    imageFileName = imageFileName;
    root = context.getExternalCacheDir();

    if (root != null && !root.exists())
        root.mkdirs();
    return new File(root, imageFileName);
}


public static FileInputStream getSourceStream(Context context, Uri u) throws FileNotFoundException {
    FileInputStream out = null;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        ParcelFileDescriptor parcelFileDescriptor =
                context.getContentResolver().openFileDescriptor(u, "r");
        FileDescriptor fileDescriptor = null;
        if (parcelFileDescriptor != null) {
            fileDescriptor = parcelFileDescriptor.getFileDescriptor();
            out = new FileInputStream(fileDescriptor);
        }
    } else {
        out = (FileInputStream) context.getContentResolver().openInputStream(u);
    }
    return out;
}

Solution 5:

Below trick worked for me, what i am doing here is if there is any authority url in URI, then i am creating a temporary image using below code & returning the Content URI of the same.

I have answered similar question here as well..

public static String getImageUrlWithAuthority(Context context, Uri uri) {
    InputStream is = null;
    if (uri.getAuthority() != null) {
        try {
            is = context.getContentResolver().openInputStream(uri);
            Bitmap bmp = BitmapFactory.decodeStream(is);
            return writeToTempImageAndGetPathUri(context, bmp).toString();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return null;
}

public static Uri writeToTempImageAndGetPathUri(Context inContext, Bitmap inImage) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage, "Title", null);
    return Uri.parse(path);
}