0

So in creating a photo portion of my app, where the user can either take a picture or choose a picture from the gallery, I am encountering some weird problems.

The "take picture" implementation relied on absolute filepaths to the cache directory in the system as given by getExternalCacheDir(), so manipulating these files is done with Strings that delineate the location of the image files.

However when I try to get a picture from the Gallery instead, using an Intent getting MediaStore.Images.Media.EXTERNAL_CONTENT_URI, the corresponding image is in the form of a Uri.

For the sake of consistency I tried to convert this Uri into a path by doing .getPath() but this only gave me the folder containing the image and not the full path to the actual image.

How do I get the full path to the image given to me from the gallery Intent so I can work purely in terms of filepaths?

KaliMa
  • 1,970
  • 6
  • 26
  • 51

1 Answers1

1

How do I get the full path to the image given to me from the gallery Intent

You don't.

There is no requirement that the MediaStore only index images that you have direct filesystem access to. In particular, for hundreds of millions of devices that run Android 4.4+ and have removable media, the MediaStore can very easily have in its index images that you cannot access from the filesystem, since you do not have arbitrary access to removable storage.

Depending on what you are doing with these images, either use an image-loading library or just use ContentResolver and methods like openInputStream() to read in the content. As a bonus, openInputStream() works with both file: and content: Uri values, so you do not necessarily need to handle your two data source types separately.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • I'm just trying to let the user choose an avatar pic (small image applied to an ImageView) either from camera or from gallery. – KaliMa Apr 13 '16 at 21:39
  • @KaliMa: If you are going to be using that image for a while (i.e., tomorrow), you will need to make a copy of it, such as to your internal storage. Your rights to use the `Uri` will elapse once your process terminates. You could try `takePersistableUriPermission()` to avoid this on API Level 19+, but you might not get peristable read permission, and that won't help on older devices. Use the `InputStream` that you get from `openInputStream()` and ordinary Java file I/O to copy the content to a file that you manage. – CommonsWare Apr 13 '16 at 21:44
  • Right now my normal "take photo" process stores the pic in the cache directory and then if the user proceeds with the update, a thumbnail is made from the cache pic and stored in internal storage (then the cache is cleared). So if I have a Uri from the Gallery instead, I can use an InputStream to copy the file (pointed to by the Uri) to the cache and then proceed the same as the Take Photo scenario? – KaliMa Apr 13 '16 at 21:57
  • @KaliMa: "I can use an InputStream to copy the file (pointed to by the Uri) to the cache and then proceed the same as the Take Photo scenario?" -- yes, though I would recommend that you copy it to your internal storage cache (`getCacheDir()`), not your external storage cache (`getExternalCacheDir()`). I don't know `MediaStore`'s indexing rules and whether it might possibly pick up your temporary copy on external storage, but if it did, that might confuse the user, until eventually the index detects that the image is no longer there (assuming you delete the copy later). – CommonsWare Apr 13 '16 at 22:44
  • I have to use external cache -- the Camera app would not store the output files in internal storage (when you use the ACTION_IMAGE_CAPTURE intent) – KaliMa Apr 13 '16 at 22:48
  • @KaliMa: For the camera path, yes, your options suck. However, your `makeThumbnailAndStoreInInternalStorage(File original)` method should not be tied to that. So, for the `Uri` scenario, use `getCacheDir()`. For the camera scenario, stick with `getExternalCacheDir()`. – CommonsWare Apr 13 '16 at 22:53
  • Is it accurate to say that if there is no external storage mounted, taking pictures from the Camera / choosing files in the Gallery should probably be disabled altogether? – KaliMa Apr 14 '16 at 00:16
  • @KaliMa: Since I haven't really considered the issue, let alone tested this scenario on any device capable of what you describe, I don't really have an opinion. Sorry! – CommonsWare Apr 14 '16 at 00:29
  • Do you mean that pretty much every device has external storage available so I am probably worrying about a really narrow edge case? – KaliMa Apr 14 '16 at 00:32
  • 1
    @KaliMa: Back in Android 2.2 and earlier timeframes, pretty much all devices had external storage that was implemented via a user-removable micro SD card. Also Android 1.x/2.x handled the USB connection via USB mass storage (think: thumb drives), which meant that if the device was plugged into a host computer, external storage was unavailable to apps. Both of those problems were cleared up by Android 3.0. There may be devices with external storage on micro SD cards now, but I don't know of any (and I've been looking...). I wouldn't trust an emulator for testing this. – CommonsWare Apr 14 '16 at 10:52
  • Just wanted to say I tried your approach and it worked very well. Once I had the Uri, I used a ContentResolver and openInputStream, and then created a FileOutputStream on the target destination File, and used this code http://stackoverflow.com/a/1574857/1855273 to do the copying. Is this the approach you had in mind? – KaliMa Apr 14 '16 at 13:15
  • 1
    @KaliMa: That answer is from Jon Skeet. By definition, it is the right answer, even if the question is "how do I bake a soufflé?" :-) But, yes, that's what I had in mind. "I imagine that based on your blogpost I shouldn't be messing with Uri.getPath() at all" -- correct. The only time you care about the innards of a `Uri` is if it is supposed to be *your* `Uri` (e.g., for your `ContentProvider`) and you need to determine what content it maps to. For a `Uri` from some other app, treat it as an opaque handle. – CommonsWare Apr 14 '16 at 13:20
  • What would I use someone else's Uri for if I am not supposed to care about its innards? – KaliMa Apr 14 '16 at 13:24
  • Re: Jon Skeet, haha, very true. When it comes to a lot of this stuff I specifically look for answers from you and Jon Skeet whenever possible. You guys definitely know your stuff and it helps me feel confident (as a beginner) to know that I'm learning good practice. – KaliMa Apr 14 '16 at 13:27
  • 1
    @KaliMa: Well, in your case, you are using it to load an image, apparently. The `Uri` that you get back from `MediaStore` is not your `Uri`. It is a `MediaStore` `Uri`. Other than caring that the scheme is not `http`/`https` (and therefore can be handled by `ContentResolver` and `openInputStream()`), you do not care about the rest of the contents, such as the path. – CommonsWare Apr 14 '16 at 13:28
  • Ahh okay, makes sense. Thank you. – KaliMa Apr 14 '16 at 13:33