I have some code where I run the method MediaStore.Images.Media.insertImage (inserting it from a source not a file name), This code saves the image to the MediaStore and returns the uri of the image. I know that when it fails for any reason it will return null instead of a uri. This image has been downloaded multiple times by many people and every once in a while it will return null from this method. I have never had this happen to me so I have no idea what is going on. What are reasons why this could happen? There is another post with the same issue but the answer is a link to the source code for MediaStore but that link goes to a page saying the link is unavailable. Any help would be appreciated. Thanks.
After removing my SD card I got this error so I know that could be a reason, I'm not sure but I feel that it would also happen if the card was full. Still just wondering if there could be another reason too.
It seems to happen when you don't have an /sdcard/DCIM/Camera directory on some versions of Android. Creating this directory (and having the permission) solved the problem for me.
MediaStore.Images.Media.insertImage is actually accessing external storage to save the image.
Some important reminders which might be causing your app to fail:
A USB connection will block SD card usage if in Mass Storage mode.
There may be other factors that might lead to an SD card being inaccessible, so make sure that you can access the SD card using a file browser first.
Make sure that your permissions are correctly configured with android.permission.WRITE_EXTERNAL_STORAGE
Posting this here for completeness, as I was getting a null from insertImage, and the cause was 1.
I faced the same issue and nothing worked. But finally after 3 hours it worked.
Solution: I found that It works fine until we delete that image from our image gallery. After that It starts returning null. But suddenly I tried changing title and description name and it worked like a charm.
So I've added a date with the title and description of bitmap. In case, user deletes bitmap manually from file manager. Still It works.
private fun insertImage(cr: ContentResolver,
source: Bitmap?,
title: String,
description: String
): String? {
val sdf = SimpleDateFormat("MM-dd-yyyy-hh.mm.ss.SSS.a", Locale.US)
val date=sdf.format(Date())
val values = ContentValues()
values.put(Images.Media.TITLE, title)
values.put(Images.Media.DISPLAY_NAME, title+date)
values.put(Images.Media.DESCRIPTION, description+date)
values.put(Images.Media.MIME_TYPE, "image/jpeg")
// Add the date meta data to ensure the image is added at the front of the gallery
values.put(Images.Media.DATE_ADDED, System.currentTimeMillis() / 1000)
values.put(Images.Media.DATE_TAKEN, System.currentTimeMillis())
var url: Uri? = null
var stringUrl: String? = null /* value to be returned */
try {
url = cr.insert(Images.Media.EXTERNAL_CONTENT_URI, values)
if (source != null) {
val imageOut = cr.openOutputStream(url!!)
try {
source.compress(Bitmap.CompressFormat.JPEG, 50, imageOut)
} finally {
imageOut!!.close()
}
} else {
cr.delete(url!!, null, null)
url = null
}
} catch (e: Exception) {
if (url != null) {
cr.delete(url, null, null)
url = null
}
}
if (url != null) {
stringUrl = url.toString()
}
return stringUrl
}
I Implemented to share news bitmap in this app and it's working fine.
Enjoy!
I ran into this issue when I was running tests on an emulator with a fresh sdcard image. I was able to solve the problem by creating /sdcard/Pictures with
new File("/sdcard/Pictures").mkdirs();
Make sure you have added permissions in your manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Then add this code before calling
MediaStore.Images.Media.insertImage
File sdcard = Environment.getExternalStorageDirectory();
if (sdcard != null) {
File mediaDir = new File(sdcard, "DCIM/Camera");
if (!mediaDir.exists()) {
mediaDir.mkdirs();
}
}
I was executing MediaStore.Images.Media.insertImage before getting user permission to WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE which was causing a null path
So make sure user has allowed for permission and then store image
When i had such a problem i fixed it with adding permissions:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Try this:
MediaStore.Images.Media.insertImage(context.getContentResolver(), bitmap, "filename", null);
Android 10 needs the actual file name on third paramter of insertImage() method.
Related
I have looked over many posts on getting SD card locations, and most that mention using Environment.getExternalStorageState(), Environment.getExternalStorageDirectory() always returns internal storage for my on a Samsung Galaxy Tab A / Android 11 (/storage/emulated/0)
I did see a comment that said to use System.getenv("SECONDARY_STORAGE"), which returns /storage/sdcard:/storage/usb1:/storage/usb2, but trying to open a file (I have put there) just returns a not found error (ie java.io.FileNotFoundException: /sdcard/external_sd/Music/test/testfile.mp3: open failed: ENOENT (No such file or directory).
Also tried Environment.getExternalStoragePublicDirectory(Environment.MEDIA_MOUNTED).getPath() and this returns /storage/emulated/0/mounted and this does not work either
I have the following permissions..
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.PERMISSIONS_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_EXTERNAL_STORAGE" />
and at startup I call
private fun verifyStoragePermissions(activity: Activity) {
// Check if we have write permission
val permission =
ActivityCompat.checkSelfPermission(activity, READ_EXTERNAL_STORAGE)
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(
activity,
arrayOf(READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE),
)
}
}
Finally, as stated here, I hardwired /sdcard/external_sd and this did not work either (ie I try to open a file I know is there, and also using the following to list contents..
File(testFolder).walk().forEach {
println(it)
}
I have other application that writes to it, so there must be some way to do it (I only want to read existing files).
What might I be doing wrong?
You can try this:
File[] dirs = getExternalFilesDirs("");
for (File currD :
dirs) {
if (currD.getAbsolutePath().equals(getExternalFilesDir("").getAbsolutePath())) {
// Here is internal storage path
} else {
// Here is SD path
}
}
With the newer Android Q many things changed, especially with scoped storage and gradual deprecation of file:/// URIs. The problem is the lack of documentation on how to handle media files correctly on Android Q devices.
I have a media file (audio) management application and I could not find yet a reliable way to tell to the OS that I performed a change to a file so that it can update its MediaStore record.
Option #1: MediaScannerService
MediaScannerConnection.scanFile(context, new String[]{ filePath }, new String[]{"audio/*"}, new MediaScannerConnection.OnScanCompletedListener() {
#Override
public void onScanCompleted(String s, Uri uri) {
}
});
Works with file:// URIs from primary storage
Not works with file:// URIs from secondary storage (such as removable storage)
Not works with any content:// URI
Option #2: broadcast
context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));
Not working at all
Soon deprecated
Option #3: manual MediaStore insertion
AudioFileContentValues are some column values from MediaStore.Audio.AudioColumns.
Old method based on file:// URI:
Uri uri = MediaStore.Audio.Media.getContentUriForPath(file_path);
newUri = context.getContentResolver().insert(uri, AudioFileContentValues);
MediaStore.Audio.Media.getContentUriForPath is deprecated
Still not working
Newer method based on what I could put together from documentation:
Uri collection = MediaStore.Audio.Media.getContentUri(correctVolume);
newUri = context.getContentResolver().insert(collection, AudioFileContentValues);
Where correctVolume would be external from primary storage, while it would be something like 0000-0000 for secondary storage, depending on where the file is located.
Insertion returns a content URI such as content://media/external/audio/media/125 but then no record is persisted inside MediaStore for files located in primary storage
Insertion fails with no URI returned and no record in MediaStore
These are more or less all the methods available in previous Android versions but none of them now allow me to notify the system that I changed some audio file metadata and to get Android to update MediaStore records. Event though option #1 is partially working, this could never be a valuable solution because it's clearly not supporting content URIs.
Is there any reliable way to trigger media scan on Android Q, despite where the file is located? We shouldn't even care about file location, according to Google, since we will soon only use content URIs. MediaStore has always been a little frustrating in my opinion, but now the situation is pretty worse.
I'm also currently struggling with that.
I think what you want to do you cannot do any longer once you are on Android Q, because you are not allowed to access the Music directory on Q. You are only allowed to create and access files in directories you created. You did not create the music directory.
Now every change to the Media has to happen threw the MediaStore. So you insert your Music file beforehand and then get an outputstream from the MediaStore to write to it. All the changes on Q on Media should be done threw the MediaStore hence you informing the MediaStore of changes cannot even occur anymore, because you never directly access the File.
This has one giant caviat in that all the new things in MediaStore that make that possible do not exist in older versions of Android. So I do currently believe that you will need to implement everything twice, sadly. At least if you want to actively influences where your music is saved to that is.
Those two MediaStore columns are new in Q and do not exist before Q, witch you'll probably need to use in Q
MediaStore.Audio.Media.RELATIVE_PATH with that you can influence the path where it's saved. So I put "Music/MyAppName/MyLibraryName" there and that will end up saving "song.mp3" into "Music/MyAppName/MyLibraryName/song.mp3"
MediaStore.Audio.Media.IS_PENDING this you should be setting to 1 while the song is still being written and then afterwards you can update it to 0.
I've also now started to implement things twice with if checks for Android versions. It's annoying. I don't want to do it. But it seems like that's the only way.
I'm just gonna put a bit of code here on how I managed inserting music on Android.Q and below. It's not perfect. I have to specify the MIME type for Q, because flacs would now become .flac.mp3 somehow, because it does not quite seem to get that.
So, anyways this is a part that I have updated already to work with Q and before, it downloads a Music file from a music player on my NAS. The app is written in kotlin, not sure if that's a problem for you.
override fun execute(library : Library, remoteApi: RemoteApi, ctx: Context) : Boolean {
var success = false
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val values = ContentValues().apply {
put(MediaStore.Audio.Media.RELATIVE_PATH, library.rootFolderRelativePath)
put(MediaStore.Audio.Media.DISPLAY_NAME, remoteLibraryEntry.getFilename())
put(MediaStore.Audio.Media.IS_PENDING, 1)
}
val collection = MediaStore.Audio.Media
.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val uri = ctx.contentResolver.insert(collection, values)
ctx.contentResolver.openOutputStream(uri!!).use {
success = remoteApi.downloadMusic(remoteLibraryEntry, it!!)
}
if(success) {
values.clear()
val songId = JDrop.mediaHelper.getSongId(uri)
JDrop.db.music.insert(Music(mediaStoreId = songId, remoteId = remoteLibraryEntry.remoteId, libraryId = library.id))
values.put(MediaStore.Audio.Media.IS_PENDING, 0)
ctx.contentResolver.update(uri, values, null, null)
} else {
ctx.contentResolver.delete(uri, null, null)
}
} else {
val file = File("${library.rootFolderPublicDirectory}/${remoteLibraryEntry.getFilename()}")
if(file.exists()) file.delete()
success = remoteApi.downloadMusic(remoteLibraryEntry, file.outputStream())
if (success) {
MediaScannerConnection.scanFile(ctx, arrayOf(file.path), arrayOf("audio/*")) { _, uri ->
val songId = JDrop.mediaHelper.getSongId(uri)
JDrop.db.music.insert(Music(mediaStoreId = songId, remoteId = remoteLibraryEntry.remoteId, libraryId = library.id))
}
}
}
return success
}
And the MediaStoreHelper Method being this here
fun getSongId(uri : Uri) : Long {
val cursor = resolver.query(uri, arrayOf(Media._ID), null, null, null)
return if(cursor != null && cursor.moveToNext()) {
val idIndex = cursor.getColumnIndex(Media._ID)
val id = cursor.getLong(idIndex)
cursor.close()
id
} else {
cursor?.close()
-1
}
}
One thing when you do not specify the MIME type it seems to assume mp3 is the MIME type. So .flac files would get saved as name.flac.mp3, because it adds the mp3 file type if there is none and it thinks it's a mp3. It does not add another .mp3 for mp3 files. I don't currently have the MIME type anywhere... so I'm gonna go ahead and do this now, I guess.
There is also a helpful google IO talk about scoped/shared storage https://youtu.be/3EtBw5s9iRY
That probably won't answer all of your questions. It sure enough didn't for me. But it was a helpful start to have a rough idea what they even did change to begin with.
For deleting and updating files its kinda the same on Q if you call delete on a mediastore entry, the file will be deleted. Before, Q you have to manually delete the file also. But if you do that on Q your app will crash. So again you have to check wether or not youre on Q or an older version of android and take appropriate actions.
I'm [still] new on android development and about Java and about Kotlin (also an explanation in Java could be ok, however, I'm studying it also, Kotlin is prefered) and I'm struggling for deleting a simple downloaded file into the ExternalStorage.
Of course I enabled permission for read & write, and, even if this code returns a "True", I still can see the untouched file into my Download folder
here the code:
___UPDATE
// uri of my file in external storage ~/Download dir
var uri = Uri.parse (Environment.getExternalStorageDirectory().getPath() + "/Download/$myFilename$myExtensionVar")
// file object pointing at uri of file in external storage
val downloadedFile = File(uri.toString())
var deletedBool:Boolean = downloadedFile.delete()
println("myTag - deleted Boolean: $deletedBool")
if (deletedBool){
println("myTag - uri of file-to-be-deleted: $uri")
var secondStepToDelete:Int = context.getContentResolver().delete(uri, null, null)
println("myTag - second Step for deletion: $secondStepToDelete")
}
The file i am trying to rid of is a multimedia file (.mp3) and I added the second block of code (the one inside the IF statement) since I found that should work, having to do with the "DocumentProvider" (I'm new and I still don't know how to proper call its methods..) but, of course, It doesn't work at all.
I think I do need the ID (long type i guess) for the file stored into the external storage, however I haven't found yet how to get it
Thanks in advance for the help!
To build a File object, use the File constructor. To build a File object for a location off of a certain root directory, use the two-parameter File(File, String) constructor:
val downloadedFile = File(Environment.getExternalStorageDirectory(), "Download/$myFilename$myExtensionVar")
Unless you are getting a Uri from DownloadManager or something, there is no Uri that you need to delete().
I have more written here on external storage and how to work with it.
In Kotlin
file.deleteRecursively()
I am using JAudioTagger library for reading and writing tags for an audio file. I am able to read the tags but unable to write them.
I am retrieving audio file path like this :
private String getSongPath(long songId) {
String path = null;
ContentResolver contentResolver = getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = {MediaStore.Audio.Media.DATA};
String selection = MediaStore.Audio.Media._ID + " == ?";
String[] selectionArgs = {String.valueOf(songId)};
Cursor cursor = contentResolver.query(uri, projection, selection, selectionArgs, null);
if (cursor != null) {
int pathCol = cursor.getColumnIndexOrThrow(projection[0]);
cursor.moveToFirst();
path = cursor.getString(pathCol);
cursor.close();
}
return path;
}
Then to write tags using JAudioTagger :
File songFile = new File(path); // path looks like /storage/3932-3434/Music/xyz.mp3
AudioFile audiofile = = AudioFileIO.read(songFile);
Tag tag = = audiofile.getTag();
tag.setField(FieldKey.TITLE, title);
// some more setField calls for different feilds
audiofile.commit();
The commit() method is giving following Exception :
org.jaudiotagger.audio.exceptions.CannotWriteException:
java.io.IOException: Cannot make changes to file xyz.mp3 at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:799) at
com.techapps.musicplayerplus.MainActivity$17.onClick(MainActivity.java:2125)
at
android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:157)
at android.os.Handler.dispatchMessage(Handler.java:102) at
android.os.Looper.loop(Looper.java:148) at
android.app.ActivityThread.main(ActivityThread.java:5417) 06-18
10:59:48.134 8802-8802/com.techapps.musicplayerplus W/System.err:
at java.lang.reflect.Method.invoke(Native Method) at
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused
by: java.io.IOException: Cannot make changes to file Saibo.mp3 at
org.jaudiotagger.audio.mp3.MP3File.precheck(MP3File.java:824) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:850) at
org.jaudiotagger.audio.mp3.MP3File.save(MP3File.java:783) at
org.jaudiotagger.audio.mp3.MP3File.commit(MP3File.java:795)
I am running this code on Android 6 while my app is targeted at SDK 22. I have also mentioned following permission in manifest.
android.permission.WRITE_EXTERNAL_STORAGE
Still I am unable to write to SD card. Please help me. Thanks in advance.
You have to use Storage Access Framework (SAF) to access SD Card from API 19 (Kitkat) onward.
First we need to ask user to provide a URI of the folder we want to access. If we want access to entire SD card, user needs to provide URI of SD card's root folder.
For example, when user hits Edit button, we have to first show hint dialog box, asking user to select required directory in SD Card which we want to access. You can display following image in hint dialog box to ask user to select root directory of SD Card :
When user dismisses hint dialog box, you need to trigger Storage Access Framework :
private void triggerStorageAccessFramework() {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}
public final void onActivityResult(final int requestCode, final int resultCode, final Intent resultData) {
if (resultCode == Activity.RESULT_OK) {
if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
Uri treeUri = null;
// Get Uri from Storage Access Framework.
treeUri = resultData.getData();
pickedDir= DocumentFile.fromTreeUri(this, treeUri);
if (!isSDCardRootDirectoryUri(treeUri)) {
Toast.makeText(this, "Wrong directory selected. Please select SD Card root directory.", Toast.LENGTH_LONG).show();
createSDCardHintDialog().show();
return;
}
// Persist URI in shared preference so that you can use it later.
SharedPreferences sharedPreferences = getSharedPreferences(App.PREFERENCE_FILENAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(App.SDCARD_URI_KEY, treeUri.toString());
editor.apply();
// Persist access permissions, so you dont have to ask again
final int takeFlags = resultData.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
getContentResolver().takePersistableUriPermission(treeUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
}
private boolean isSDCardRootDirectoryUri(Uri treeUri) {
String uriString = treeUri.toString();
return uriString.endsWith("%3A");
}
Once you get Uri of user picked directory, you can perform write operation using SAF : (creadit : this answer )
public void writeFile(DocumentFile pickedDir) {
try {
DocumentFile file = pickedDir.createFile("image/jpeg", "try2.jpg");
OutputStream out = getContentResolver().openOutputStream(file.getUri());
try {
// write the image content
} finally {
out.close();
}
} catch (IOException e) {
throw new RuntimeException("Something went wrong : " + e.getMessage(), e);
}
}
It could be that you pointing to non existing file.
Check your path file by using Log.
Log.d("Activity", "path = " + path);
Android-M or API 23 introduced Runtime Permissions for reducing security flaws in android device.
To update your apps using Google Play services to handle Android 6.0 permissions, it’s good practice to manage the user’s expectations in setting permissions that the runtime may require. The following link will help you avoid potential issues.
https://developer.android.com/training/permissions/requesting.html
have you declared the permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ?
I saw that you already created an issue in the JAudioTagger GitHub repository which was advisable, but never got a universally working solution. My findings so far:
The answer mentioning SAF is correct, but it won't help you as SAF will provide a DocumentFile, not a File.
You might try to modify JAudioTagger to your needs, replacing File with DocumentFile, but the latter one has not all functions you will need.
Also InputStream and OutputStream will not help you, as JAudioTagger needs File and internally heavily uses RandomAccessFile which is not available either.
Google "forgot" to provide some getRandomAccessFileFromUri() which makes things even worse (Yes, there are hacks using Java reflection to work around this limitation...).
The "/proc/self/fd" method (How to handle SAF when I can only handle File or file-path?) will also not work immediately, as JAudioTagger needs copy and renaming functions that are not applicable to this kind of files. Particularly JAudioTagger will not find a suitable file name extension like ".m4a". Of course you could try to change JAudioTagger accordingly.
You might follow the advice to make a copy of the file to your personal storage, then apply JAudioTagger to it and finally copy it back to SD card, but:
If you want to use JAudioTagger to read from SD card, this will, as announced by Google, fail with Android 10. Starting with that version, you will not even have read access to the SD card via the File interface.
Further, the File interface gives you read access to SD cards with Android 9 and below, but not to other SAF devices, like USB OTG memory or SMB shares etc.
Of course you could also copy each file in order to read its metadata, but this will be awfully slow and is not suitable if you have more than a few files.
So my current advices are:
Try the "/proc/self/fd" method and modify JAudioTagger accordingly.
If the changes are too heavy, use the fd method for reading the tags and the copy method for writing.
BTW: I am currently modifying an older version of JAudioTagger for using both File and DocumentFile transparently, but the changes are tremendous, bear a high risk, need some help classes, and the work is unfinished, yet.
BTSW: The DocumentFile functions are painfully slow, compared to the File functions.
Since Android 4.2 if a user downloads some file in the browser the DownloadManager is used. If the user clicks the 'download complete' notification an Intent is and was always launched. Before Android 4.2 the intent used to have the downloaded file's path in the content, such that:
intent.getData()
would return a String such as file:///storage/emulated/0/Download/some_file.ext. However, since Android 4.2 the download manager broadcasts and intent with a content scheme, such as content://downloads/all_downloads/2334.
How do I retrieve the local file path for a downloaded file?
I've tried the following:
public static String getRealPathFromURI(Uri contentUri, Activity activity) {
DownloadManager downloadManager = (DownloadManager) activity.getSystemService(Activity.DOWNLOAD_SERVICE);
String[] contentParts = contentUri.getEncodedPath().split("/");
Cursor q = downloadManager.query(new DownloadManager.Query().setFilterById(Integer.parseInt(contentParts[contentParts.length - 1])));
if (q == null) {
// Download no longer exists
return null;
}
q.moveToFirst();
return q.getString(q.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
}
But the cursor never returns any rows (so q.getCount() == 0 and therefor the last return statement throws an exception). Also, the hack by parsing the download file id from the Uri seems odd.
UPDATE: I have also tried:
input = getActivity().getContentResolver().openInputStream(contentUri);
but this returns an error stating
Permission Denial: reading com.android.providers.downloads.DownloadProvider uri content://downloads/all_downloads/2334 from pid=30950, uid=10064 requires android.permission.ACCESS_ALL_DOWNLOADS, or grantUriPermission()
Clearly I can't access the downloads (as my app did not initiate them - the browser did) through the ContentProvider.
Here's what worked. First, you can't (and shouldn't want to) get the file path as botteaap correctly pointed out. (Credits to him for setting me on the right path.) Instead you get a temporary permission automatically to access the file content, using:
InputStream input = getContentResolver().openInputStream(intent.getData());
You can read this InputStream like any other in Java. It seems there is no way to get the file name. If you need a file, first write the input stream to a (temporary) file.
The SecurityException is throw when your temporary access was revoked. This happend for me on some files that I tried to read incorrectly before and specifically when the Intent was picked up in my Acticity's onNewIntent.
Getting it through the content resolver is the right thing. Not every content url is going to be a file. For example, the Gallery app will give you uri's that translate to a network call or a local file depending on the source.
Even if you'd get to the real file path, you'll probably unable to read it, due to file permissions, although you can be lucky it it's on external storage. Have you tried adding android.permission.ACCESS_ALL_DOWNLOADS to your app like the exception suggests? That won't work, since the permission is at signature level :(
I just want to add to the answer from #erickok as it took me several hours to figure this out. As stated by #jcesarmobile, you are only guaranteed to be able to get the name and size of the file, not the full path.
You can get the name and size as follows, and then save to a temp file:
String filename = null;
Long filesize = null;
Cursor cursor = null;
try {
cursor = this.getContentResolver().query(intent.getData(), new String[] {
OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}, null, null, null );
if (cursor != null && cursor.moveToFirst()) {
filename = cursor.getString(0);
filesize = cursor.getLong(1);
}
} finally {
if (cursor != null)
cursor.close();
}