5

In my application users select picture from gallery and upload. On many devices my code works perfect but on new devices (etc: samsung S5, LG G2, Samsung S4) application crashes when selecting photo. I uploaded application to Google Play Store and downloaded these devices then sended report to google play when app crashes. This is my select picture and resize code.

Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, "Complete action using"), 1); 

public Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
    int width = bm.getWidth();
    int height = bm.getHeight();
    float scaleWidth = ((float) newWidth) / width;
    float scaleHeight = ((float) newHeight) / height;
    // create a matrix for the manipulation
    Matrix matrix = new Matrix();
    // resize the bit map
    matrix.postScale(scaleHeight, scaleHeight);
    // recreate the new Bitmap
    Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
    return resizedBitmap; }

 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 if (requestCode == 1 && resultCode == RESULT_OK) {
        //Bitmap photo = (Bitmap) data.getData().getPath(); 

     Uri selectedImageUri = data.getData();
     imagepath = getPath(selectedImageUri);

     Bitmap myBitmap = getResizedBitmap(BitmapFactory.decodeFile(imagepath),200,200);

     try {
         ExifInterface exif = new ExifInterface(imagepath);
         orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, 1);
         Log.d("EXIF", "Exif: " + orientation);
         resimyon=Integer.toString(orientation);
         Matrix matrix = new Matrix();
         if (orientation == 6) {
             matrix.postRotate(90);
         }
         else if (orientation == 3) {
             matrix.postRotate(180);
         }
         else if (orientation == 8) {
             matrix.postRotate(270);
         }
         myBitmap = Bitmap.createBitmap(myBitmap, 0, 0, myBitmap.getWidth(), myBitmap.getHeight(), matrix, true); // rotating bitmap
     }
     catch (Exception e) {

     }


     imageview.setImageBitmap(myBitmap);
     messageText.setText("Yüklenecek dosya:" +imagepath);
     uploadButton.setEnabled(true);

 } 

These are the reports came from Samsung S5 and LG G2.

SAMSUNG S5

java.lang.SecurityException
konum: android.os.Parcel.readException

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1,     data=Intent { dat=content://media/external/images/media/1909 (has extras) }} to activity    {com.turk.bakistik/com.turk.bakistik.Profilresimyukle}: java.lang.SecurityException: Permission    Denial: reading com.android.providers.media.MediaProvider uri   content://media/external/images/media/1909 from pid=29038, uid=10236 requires    android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.app.ActivityThread.deliverResults(ActivityThread.java:3663)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3706)
at android.app.ActivityThread.access$1400(ActivityThread.java:173)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1351)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5579)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1268)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1084)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.SecurityException: Permission Denial: reading      com.android.providers.media.MediaProvider uri content://media/external/images/media/1909 from pid=29038, uid=10236 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1465)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:185)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:413)
at android.content.ContentResolver.query(ContentResolver.java:464)
at android.content.ContentResolver.query(ContentResolver.java:407)
at android.app.Activity.managedQuery(Activity.java:1898)
at com.turk.bakistik.Profilresimyukle.getPath(Profilresimyukle.java:209)
at com.turk.bakistik.Profilresimyukle.onActivityResult(Profilresimyukle.java:173)
at android.app.Activity.dispatchActivityResult(Activity.java:5643)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3659)
... 11 more  

LG G2

java.lang.NullPointerException
konum: com.turk.bakistik.Profilresimyukle.getResizedBitmap
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.android.providers.media.documents/document/image:41 flg=0x1 }} to activity {com.turk.bakistik/com.turk.bakistik.Profilresimyukle}: java.lang.NullPointerException
at android.app.ActivityThread.deliverResults(ActivityThread.java:3382)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3425)
at android.app.ActivityThread.access$1300(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5105)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.turk.bakistik.Profilresimyukle.getResizedBitmap(Profilresimyukle.java:123)
at com.turk.bakistik.Profilresimyukle.onActivityResult(Profilresimyukle.java:175)
at android.app.Activity.dispatchActivityResult(Activity.java:5467)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3378)
... 11 more

How can I fix this problem?

sipdorus
  • 963
  • 1
  • 12
  • 28
  • 1
    added into mainfest android.permission.READ_EXTERNAL_STORAGE – Naveen Tamrakar Oct 14 '14 at 07:59
  • 1
    I remember I encountered this problem before. It was caused by different, unrecognizable URI that is returned by the app (as shown in your stack trace `dat=...`). For Samsung S5: [this question](http://stackoverflow.com/questions/20028319/how-to-convert-content-media-external-images-media-y-to-file-storage-sdc). For LG G2: [this question](http://stackoverflow.com/questions/19834842/android-gallery-on-kitkat-returns-different-uri-for-intent-action-get-content) – Andrew T. Oct 14 '14 at 08:05

3 Answers3

22

Because since android kitkat, you can not get pick image local path as normal way. Please check device android version, if version < kitkat, you can use your way. If version is kitkat or later, you can try this code to get file path, then you can convert to bitmap or anything you want.

/**
 * Get a file path from a Uri. This will get the the path for Storage Access
 * Framework Documents, as well as the _data field for the MediaStore and
 * other file-based ContentProviders.
 *
 * @param context The context.
 * @param uri The Uri to query.
 * @author paulburke
 */
public static String getPath(final Context context, final Uri uri) {

    final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;

    // DocumentProvider
    if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
        // ExternalStorageProvider
        if (isExternalStorageDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            if ("primary".equalsIgnoreCase(type)) {
                return Environment.getExternalStorageDirectory() + "/" + split[1];
            }

            // TODO handle non-primary volumes
        }
        // DownloadsProvider
        else if (isDownloadsDocument(uri)) {

            final String id = DocumentsContract.getDocumentId(uri);
            final Uri contentUri = ContentUris.withAppendedId(
                    Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));

            return getDataColumn(context, contentUri, null, null);
        }
        // MediaProvider
        else if (isMediaDocument(uri)) {
            final String docId = DocumentsContract.getDocumentId(uri);
            final String[] split = docId.split(":");
            final String type = split[0];

            Uri contentUri = null;
            if ("image".equals(type)) {
                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
            } else if ("video".equals(type)) {
                contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
            } else if ("audio".equals(type)) {
                contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
            }

            final String selection = "_id=?";
            final String[] selectionArgs = new String[] {
                    split[1]
            };

            return getDataColumn(context, contentUri, selection, selectionArgs);
        }
    }
    // MediaStore (and general)
    else if ("content".equalsIgnoreCase(uri.getScheme())) {

        // Return the remote address
        if (isGooglePhotosUri(uri))
            return uri.getLastPathSegment();

        return getDataColumn(context, uri, null, null);
    }
    // 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.
 */
public 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 index = cursor.getColumnIndexOrThrow(column);
            return cursor.getString(index);
        }
    } finally {
        if (cursor != null)
            cursor.close();
    }
    return null;
}


/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is ExternalStorageProvider.
 */
public static boolean isExternalStorageDocument(Uri uri) {
    return "com.android.externalstorage.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is DownloadsProvider.
 */
public static boolean isDownloadsDocument(Uri uri) {
    return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is MediaProvider.
 */
public static boolean isMediaDocument(Uri uri) {
    return "com.android.providers.media.documents".equals(uri.getAuthority());
}

/**
 * @param uri The Uri to check.
 * @return Whether the Uri authority is Google Photos.
 */
public static boolean isGooglePhotosUri(Uri uri) {
    return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
Quoc Truong
  • 545
  • 4
  • 9
  • working perfect on LG G2. but I will test on S5 this evening. I hope it works. But I have another problem. I am resizing image `Bitmap myBitmap = getResizedBitmap(BitmapFactory.decodeFile(imagepath),200,200); imageview.setImageBitmap(myBitmap);` but when uploading I am using imagepath. so upload takes a lot of time. How can I upload resized image? Thanks for help – sipdorus Oct 14 '14 at 08:35
  • @sipdorus: When you got file path, you can use this code to convert file to bitmap, then call your resize method: BitmapFactory.Options options = new BitmapFactory.Options(); options.inPreferredConfig = Bitmap.Config.ARGB_8888; Bitmap bitmap = BitmapFactory.decodeFile(photoPath, options); – Quoc Truong Oct 14 '14 at 08:39
  • sorry I didn't understand – sipdorus Oct 14 '14 at 08:45
  • When you got file path, you can convert file to bitmap, then use your method to resize bitmap, after that, you can create temporary file from resized bitmap and upload it. – Quoc Truong Oct 14 '14 at 08:51
  • Will temporary created file give me a filepath? I need file path because my upload code needs filepath. If it gives me filepath, will I need write to external storage permission and will user see resized picture at the gallery like whatsapp? – sipdorus Oct 14 '14 at 09:00
  • You can take a look on http://stackoverflow.com/questions/3425906/creating-temporary-files-in-android – Quoc Truong Oct 14 '14 at 09:03
  • in the code above DocumentsContract.isDocumentUri(... gives error. and says Call requires api level 19 (current min is 8) so my app crashs. – sipdorus Oct 14 '14 at 10:40
  • Please see this link: http://stackoverflow.com/questions/16243744/calling-api-from-higher-api-level-than-the-minimum-requirement – Quoc Truong Oct 14 '14 at 10:54
  • This is crashing when I select the new Photos app or Google Drive. – Rahul Sainani Mar 08 '15 at 18:15
  • I think the provided code would crash on pre 4.4 devices because Build.VERSION_CODES.KITKAT does not exist. You should use a hard-coded constant. – Endor Jul 29 '15 at 06:38
  • Crash also for me, when I select an image from Google Drive. Somebody solve this? – Simone Sessa Dec 02 '15 at 21:01
2

This code goes in your onActivityResult:

AsyncTask<Uri, Void, Bitmap> imageLoadAsyncTask = new AsyncTask<Uri, Void, Bitmap>() {

    @Override
    protected Bitmap doInBackground(Uri... uris) {
        return getBitmapFromUri(uris[0]);
    }

    @Override
        protected void onPostExecute(Bitmap bitmap) {
        //crop, resize, play, do whatever with the bitmap
    }
};

imageLoadAsyncTask.execute(data.getData());

This is getting called from AsyncTask because sometimes the images/files might not be stored locally like images backed up on Google+ or Google Drive. As recommended in this Devbyte video, always call it on the background thread.

private Bitmap getBitmapFromUri(Uri uri){
    ParcelFileDescriptor parcelFileDescriptor = null;

    try {
        parcelFileDescriptor = getContentResolver().openFileDescriptor(uri, "r");

        FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
        Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);

        parcelFileDescriptor.close();

        return bitmap;
    } catch (IOException e) {
        Log.e(tag, "Failed to load image.", e);
        e.printStackTrace();
        return null;
    } finally{
        try {
            if (parcelFileDescriptor != null) {
                parcelFileDescriptor.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
            Log.e(tag, "Error closing ParcelFile Descriptor");
        }
    }

}

This is from the sample code for Storage Client Sample and I've tested it on Kitkat and Lollipop.

Will test on older versions and update. If anyone tests this on older version, let me know.

Update: Tested on JellyBean and it works.

Rahul Sainani
  • 3,437
  • 1
  • 34
  • 48
0

in this tutorial used an util class which is handling all types of images from different sources. https://www.simplifiedcoding.net/upload-pdf-file-server-android/ you have to do just, in on onActivityResult method String path = FilePath.getPath(this, filePath); you will get right path, across every device and every source of app which are providing images(FilePath class will available in above tutorial link)

Mohd Qasim
  • 896
  • 9
  • 20