According to the docs file path access is granted in Android R:
Starting in Android 11, apps that have the READ_EXTERNAL_STORAGE permission can read a device's media files using direct file paths and native libraries. This new capability allows your app to work more smoothly with third-party media libraries.
The problem is that I can't get the file path from MediaStore
, so how are we supposed to read a file path that we can't access/retrieve? Is there a way, I'm not aware of, that we can get the file path from MediaStore
?
Furthermore, the docs say the following:
All Files Access
Some apps have a core use case that requires broad file access, such as file management or backup & restore operations. They can get All Files Access by doing the following:
- Declare the MANAGE_EXTERNAL_STORAGE permission.
- Direct users to a system settings page where they can enable the Allow access to manage all files option for your app.
This permission grants the following:
- Read access and write access to all files within shared storage.
- Access to the contents of the MediaStore.Files table.
But I do not need all file access, I only want the user to select a video from MediaStore
and pass the file path to FFmpeg
(it requires a file path). I know that I can no longer use the _data
column to retrieve a file path.
Please note:
Uri
is returned from MediaStore
and does not point to a file.FFmpeg
, but I could do that before Android R.FileDescriptor
to FFmpeg
and I can not use /proc/self/fd/
(I get /proc/7828/fd/70: Permission denied
when selecting a file from the SD Card), have a look at this issue.So what am I supposed to do, am I missing something? What was meant with can read a device's media files using direct file paths and native libraries
?
On the Settings > Privacy > Permission manager > Files and media page, each app that has the permission is listed under Allowed for all files. If your app targets Android 11, keep in mind that this access to "all files" is read-only.
The reason while the android 11 OBB folder was restricted from being accessed was due to the fact that android wanted to stop files from interacting with each other, basically separating them in a container, while also stopping the user from manually making changes to files or app data.
After asking a question on issuetracker, I've come to the following conclusions:
File
restrictions that were added in Android Q is removed. So we can once again access File
objects.If you are targeting Android 10 > and you want to access/use file paths, you will have to add/keep the following in your manifest:
android:requestLegacyExternalStorage="true"
This is to ensure that file paths are working on Android 10(Q). On Android R this attribute will be ignored.
Don't use DATA column for inserting or updating into Media Store, use DISPLAY_NAME
and RELATIVE_PATH
, here is an example:
ContentValues valuesvideos;
valuesvideos = new ContentValues();
valuesvideos.put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/" + "YourFolder");
valuesvideos.put(MediaStore.Video.Media.TITLE, "SomeName");
valuesvideos.put(MediaStore.Video.Media.DISPLAY_NAME, "SomeName");
valuesvideos.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
valuesvideos.put(MediaStore.Video.Media.DATE_ADDED, System.currentTimeMillis() / 1000);
valuesvideos.put(MediaStore.Video.Media.DATE_TAKEN, System.currentTimeMillis());
valuesvideos.put(MediaStore.Video.Media.IS_PENDING, 1);
ContentResolver resolver = getContentResolver();
Uri collection = MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
Uri uriSavedVideo = resolver.insert(collection, valuesvideos);
You can no longer use the ACTION_OPEN_DOCUMENT_TREE
or the ACTION_OPEN_DOCUMENT
intent action to request that the user select individual files from Android/data/
,Android/obb/
and all sub-directories.
File
objects when you need to perform "seeking", like when using FFmpeg
, for example.If you want to access a File
or want a file path from a Uri
that was returned from MediaStore
, I've created a library that handles all the exceptions you might get. This includes all files on the disk, internal and removable disk. When selecting a File
from Dropbox, for example, the File
will be copied to your applications directory where you have full access, the copied file path will then be returned.
For getting path, i'm coping file with fileDescriptor to new path & i use that path.
Finding File Name:
private static String copyFileAndGetPath(Context context, Uri realUri, String id) {
final String selection = "_id=?";
final String[] selectionArgs = new String[]{id};
String path = null;
Cursor cursor = null;
try {
final String[] projection = {"_display_name"};
cursor = context.getContentResolver().query(realUri, projection, selection, selectionArgs,
null);
cursor.moveToFirst();
final String fileName = cursor.getString(cursor.getColumnIndexOrThrow("_display_name"));
File file = new File(context.getCacheDir(), fileName);
FileUtils.saveAnswerFileFromUri(realUri, file, context);
path = file.getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (cursor != null)
cursor.close();
}
return path;
}
Copy With File Descriptor:
fun saveAnswerFileFromUri(uri: Uri, destFile: File?, context: Context) {
try {
val pfd: ParcelFileDescriptor =
context.contentResolver.openFileDescriptor(uri, "r")!!
if (pfd != null) {
val fd: FileDescriptor = pfd.getFileDescriptor()
val fileInputStream: InputStream = FileInputStream(fd)
val fileOutputStream: OutputStream = FileOutputStream(destFile)
val buffer = ByteArray(1024)
var length: Int
while (fileInputStream.read(buffer).also { length = it } > 0) {
fileOutputStream.write(buffer, 0, length)
}
fileOutputStream.flush()
fileInputStream.close()
fileOutputStream.close()
pfd.close()
}
} catch (e: IOException) {
Timber.w(e)
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With