Before KitKat (or before the new Gallery) the Intent.ACTION_GET_CONTENT
returned a URI like this
content://media/external/images/media/3951.
Using the ContentResolver
and quering for
MediaStore.Images.Media.DATA
returned the file URL.
In KitKat however the Gallery returns a URI (via "Last") like this:
content://com.android.providers.media.documents/document/image:3951
How do I handle this?
This worked fine for me:
@paul burke's answer works fine for both camera and gallery pictures for API level 19 and above, but it doesn't work if your Android project's minimum SDK is set to below 19, and some answers referring above doesn't work for both gallery and camera. Well, I have modified @paul burke's code which works for API level below 19. Below is the code.
I believe the responses already posted should get people going in the right direction. However here is what I did that made sense for the legacy code I was updating. The legacy code was using the URI from the gallery to change and then save the images.
Prior to 4.4 (and google drive), the URIs would look like this: content://media/external/images/media/41
As stated in the question, they more often look like this: content://com.android.providers.media.documents/document/image:3951
Since I needed the ability to save images and not disturb the already existing code, I just copied the URI from the gallery into the data folder of the app. Then originated a new URI from the saved image file in the data folder.
Here's the idea:
Note - copyAndClose() just does file I/O to copy InputStream into a FileOutputStream. The code is not posted.
This is a total hack, but here's what I did...
So while playing with setting up a DocumentsProvider, I noticed that the sample code (in
getDocIdForFile
, around line 450) generates a unique id for a selected document based on the file's (unique) path relative to the specified root you give it (that is, what you setmBaseDir
to on line 96).So the URI ends up looking something like:
content://com.example.provider/document/root:path/to/the/file
As the docs say, it's assuming only a single root (in my case that's
Environment.getExternalStorageDirectory()
but you may use somewhere else... then it takes the file path, starting at the root, and makes it the unique ID, prepending "root:
". So I can determine the path by eliminating the"/document/root:
" part from uri.getPath(), creating an actual file path by doing something like this:I know. It's shameful, but it worked. Again, this relies on you using your own documents provider in your app to generate the document ID.
(Also, there's a better way to build the path that don't assume "/" is the path separator, etc. But you get the idea.)
I've tried several of the answers here, and I think I have a solution that will work every time and manages permissions as well.
It is based on the clever solution from LEO. This post should contain all the code you need to make this work, and it should work on any phone and Android version ;)
In order to have the ability to pick a file from an SD card, you'll need this in your manifest:
Constants:
Check permission and launchImagePick if possible
Permission response
Manage permission response
Launch image pick
Manage Image pick response
That's all folks; this works for me on all the telephones I have.
Just wanted to say that this answer is brilliant and I'm using it for a long time without problems. But some time ago I've stumbled upon a problem that DownloadsProvider returns URIs in format
content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2Fdoc.pdf
and hence app is crashed withNumberFormatException
as it's impossible to parse its uri segments as long. Butraw:
segment contains direct uri which can be used to retrieve a referenced file. So I've fixed it by replacingisDownloadsDocument(uri)
if
content with following: