15

I tried to take a photo and then to use the photo. Here is what I did.

My device was Nexus 6P (Android 7.1.1).

First, I created a Uri:

Uri mPicPath = UriUtil.fromFile(this, UriUtil.createTmpFileForPic());
//Uri mPicPath = UriUtil.fromFile(this, UriUtil.createFileForPic());

And then, I started Intent:

Intent intent = ActivityUtils.getTakePicIntent(mPicPath);
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivityForResult(intent, RequestCode.TAKE_PIC);
}

At last, I handled this Uri on onActivityResult:

if (requestCode == RequestCode.TAKE_PIC) {
    if (resultCode == RESULT_OK && mPicPath != null) {
        Bitmap requireBitmap = BitmapFactory.decodeFile(mPicPath.getPath());
        //path is like this: /Download/Android/data/{@applicationId}/files/Pictures/JPEG_20170216_173121268719051242.jpg
        requireBitmap.recycle();//Here NPE was thrown.
    }
}

At the meantime, Here are UriUtil:

public class UriUtil {

    public static File createFileForPic() throws IOException {
        String fileName = "JPEG_" + new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault()).format(new Date()) + ".jpg";
        File storageDic = SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        return new File(storageDic, fileName);
    }

    public static File createTmpFileForPic() throws IOException {
        String fileName = "JPEG_" + new SimpleDateFormat("yyyyMMdd_HHmmssSSS", Locale.getDefault()).format(new Date());
        File storageDic = SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        return File.createTempFile(fileName, ".jpg", storageDic);
    }

    public static Uri fromFile(@NonNull Context context, @NonNull File file) {
        if (context == null || file == null) {
            throw new RuntimeException("context or file can't be null");
        }
        if (ActivityUtils.requireSDKInt(Build.VERSION_CODES.N)) {
            return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".file_provider", file);
        } else {
            return Uri.fromFile(file);
        }
    }
}

and getTakePicIntent(Uri):

public static Intent getTakePicIntent(Uri mPicPath) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, mPicPath);
    if (!ActivityUtils.requireSDKInt(Build.VERSION_CODES.KITKAT_WATCH)) {//in pre-KitKat devices, manually grant uri permission.
        List<ResolveInfo> resInfoList = SPApplication.getInstance().getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            SPApplication.getInstance().grantUriPermission(packageName, mPicPath, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    } else {
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    }
    return intent;
}

and requireSDKInt:

public static boolean requireSDKInt(int sdkInt) {
    return Build.VERSION.SDK_INT >= sdkInt;
}

Everything worked on different Android API except on Android Nougat(7.x.x). Even 'FileProvider' was provided, 'requireBitmap' always be returned as 'null'.

After logs read, FileNotFoundException was thrown from BitmapFactory. It was like:

BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: /Download/Android/data/{@applicationId}/files/Pictures/JPEG_20170216_1744551601425984925.jpg (No such file or directory)

It seems all is clear, but I still can't understand that.

How could it be? Clearly I created a File! How could I solve that? Any ideas?

SilentKnight
  • 13,761
  • 19
  • 49
  • 78
  • `After logs read, FileNotFoundException was thrown from BitmapFactory.`. Yes but it will mention the used path too! Why did you omit it? `decodeFile(mUri.getPath())`. Please tell the value of mUri.getPath(). All works on 6.0 ? – greenapps Feb 16 '17 at 08:55
  • `File.createTempFile`. Do NOT create a temporary file. Only create a file name. The file will be created by the camera app. – greenapps Feb 16 '17 at 08:58
  • `Uri mPicPath` AND `mUri.getPath())` ?????? Where does mUri come from? – greenapps Feb 16 '17 at 09:07
  • `intent.addFlags()`. Are not needed. Not for any Android version. They do always nothing. Confusing code. – greenapps Feb 16 '17 at 09:10
  • @greenapps I have updated my post. At the meantime, value of `mPicUri.getPath()` is like: "/Download/Android/data/{@applicationId}/files/Pictures/JPEG_20170216_173121268719051242.jpg". Even `UriUtil.createFileForPic()` doesn't work. `Intent.FLAG_GRANT_READ_URI_PERMISSION` don't work indeed. And all works on pre-7.0. – SilentKnight Feb 16 '17 at 09:38
  • `"/Download/Android/data/{@applicationId}/files/Pictures/........"` That would be an non existend nonsense path. Check if the directory `storageDic` exists before use. A full path can never start with /Download/.... `SPApplication.getInstance().getExternalFilesDir()` is implemented wrong. – greenapps Feb 16 '17 at 10:25
  • @SilentKnight try this https://github.com/raghunandankavi2010/SamplesAndroid/tree/master/StackOverFlowTest. – Raghunandan Apr 24 '17 at 10:55
  • Maybe this can help. It was my solution. https://es.stackoverflow.com/questions/206632/buena-resoluci%C3%B3n-de-imagen-ralentiza-la-app-cargar-imagen-desde-uri-soluciona – Mattias Peralta Oct 21 '18 at 22:23

6 Answers6

16

I tried your code. Here's the sample of my try.https://github.com/raghunandankavi2010/SamplesAndroid/tree/master/StackOverFlowTest.

Have a look at this blog https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html

In the blog commonsware mentions that you should not do new File (mPicPath.getPath()).

Instead you should use the below in onActivityResult

try {
       InputStream ims = getContentResolver().openInputStream(mPicPath);
       // just display image in imageview
       imageView.setImageBitmap(BitmapFactory.decodeStream(ims));
    } catch (FileNotFoundException e) {
            e.printStackTrace();
    }

And the xml

 <external-files-path name="external_files" path="path" />

Note: its a content uri that you have. On my phone i get the uri as below. Tested only on Nexus6p.

content://com.example.raghu.stackoverflowtest.fileProvider/external_files/Pictures/JPEG_20170424_161429691143693160.jpg

More on file provider https://developer.android.com/reference/android/support/v4/content/FileProvider.html

Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • 2
    Yeah, finally, only `BitmapFactory.decodeStream(inputStream)` worked. – SilentKnight Apr 25 '17 at 02:52
  • When using a "content://..." path like that, I can load a chosen png file using Glide, whereas, using BitmapFactory.loadFile(uri), this class returns with a file not found expection, stating, ODDLY ENOUGH, that the path not found is "content:/…" with ONE forward slash only. Why is BitmapFactory removing one of the slashes, only to fail? Mystic mysterium! – carl Nov 18 '19 at 11:57
3

Try this function may be you get path from it. This is works for me

@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getRealPathFromURI_API19(Context context, Uri uri) {

    if (HelperFunctions.isExternalStorageDocument(uri)) {

        // ExternalStorageProvider
        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];
        }
    } else if (HelperFunctions.isDownloadsDocument(uri)) {

        // DownloadsProvider

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

        return HelperFunctions.getDataColumn(context, contentUri, null, null);

    } else if (HelperFunctions.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 HelperFunctions.getDataColumn(context, contentUri, selection,
                selectionArgs);


    } else if ("content".equalsIgnoreCase(uri.getScheme())) {

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

        return HelperFunctions.getDataColumn(context, uri, null, null);
    }
    // File
    else if ("file".equalsIgnoreCase(uri.getScheme())) {
        return uri.getPath();
    }

    return null;

}

here is my static function HelperFunction class which use above.

 public class HelperFunction{
       /**
 * @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());
}

}

Vasudev Vyas
  • 726
  • 1
  • 10
  • 28
  • 1
    Just a heads up, you left out the code for the HelperFunctions.getDataColumn() function. This would not work without it. – PGMacDesign Jul 31 '17 at 20:30
2

It looks very much like {@applicationID} is actually supposed to contain the package id of your application. As it is, the folder doesn't exist so the file can't be written or read. it looks like SPApplication.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES); is not returning a valid path.

C B J
  • 1,838
  • 16
  • 22
2

Nougat and Previous Update Is Working With This Code

To get the real path from database in nougat use this function and pass the uri which you are getting in data field as in onActivityResult and then get the file from the path.

    public String getPath(Uri uri) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    String document_id = cursor.getString(0);
    document_id = document_id.substring(document_id.lastIndexOf(":") + 1);
    cursor.close();

    cursor = getContentResolver().query(
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            null, MediaStore.Images.Media._ID + " = ? ", new String[]{document_id}, null);
    cursor.moveToFirst();
    String path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
    cursor.close();

    return path;
}
2

You should use FileProvider. You may refer to this commit for required change.

Fung
  • 961
  • 1
  • 8
  • 17
2

Try Glide.

1. Add Glide dependency to app/build.gradle

repositories {
   mavenCentral() // jcenter() works as well because it pulls from Maven Central
}

dependencies {
   compile 'com.github.bumptech.glide:glide:3.7.0'
   compile 'com.android.support:support-v4:19.1.0'
 }

2. Load image using Glide

Glide.with(context).load(new File(uri.getPath())).placeholder(R.drawable.placeholder).into(imageView);

OR

Glide.load(new File(uri.getPath())) // Uri of the picture
.transform(new CircleTransform(..))
.into(imageView);
PEHLAJ
  • 9,980
  • 9
  • 41
  • 53