If you are targeting to Android 11 API, you cannot directly get access to the file paths, as there are many restrictions in API 30(Android R). As scoped storage API was introduced in Android 10(API 29), the storage is now divided into scoped storage (private storage) and shared storage (public storage). Scoped storage is a kind you can only have access to the files that are created in your scoped storage directory(i.e.
/Android/data/ or /Android/media/<your-package-name>
). You cannot access files from shared storage (i.e. internal storage/external SD card storage etc.)
The shared storage is again further divided into Media and Download collection. Media collection stores Image, Audio and Video files. Download collection would take care of non-media files.
To learn in more details about scoped storage and shared storage refer this link: Scoped Storage in Android 10 & Android 11 .
If you are dealing with Media files (i.e. Images, Videos, Audio) you can get the file path by Using Media Store API that having support to API 30(Android 11). and If you are dealing with non-media files(i.e. documents and other files) you can get the file path by using file Uri.
Note: If you are using the file or Uri util classes (such as RealPathUtil, FilePathUtils etc.) to get the file path, here you can get the desired file path but you cannot read that file, as it will throw an exception of Read Access (as Permission denied) in Android 11, as you cannot read the files that are created by another application.
So to achieve this scenario of getting the file path in Android 11(API 30), It a recommended to copy the file into the cache directory of your application using File Uri and get the path of the file access from cache directory.
Here in my scenario I have used both APIs to get the file access in Android 11. To get the file path of the media files (i.e. Images, Videos, Audio), I've used the Media Store API (Refer this link: Media Store API Example - Access media files from shared storage ), and to get the file path of the non-media files (i.e. Documents and other files), I've used fileDescriptor.
File Descriptor Example:
I have created the system dialog file picker to pick the file.
private fun openDocumentAction() {
val mimetypes = arrayOf(
"application/*", //"audio/*",
"font/*", //"image/*",
"message/*",
"model/*",
"multipart/*",
"text/*"
)
// you can customize the mime types as per your choice.
// Choose a directory using the system's file picker.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
//type = "application/pdf" //only pdf files
type = "*/*"
putExtra(Intent.EXTRA_MIME_TYPES, mimetypes)
addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when it loads.
//putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, RC_SAF_NON_MEDIA)
}
And handled the result of file picker in onActivityResult method of the activity. Get the file URI at here.
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
when (requestCode) {
RC_SAF_NON_MEDIA -> {
//document selection by SAF(Storage Access Framework) for Android 11
if (resultCode == RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
data?.data?.also { uri ->
//Permission needed if you want to retain access even after reboot
contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
// Perform operations on the document using its URI.
val path = makeFileCopyInCacheDir(uri)
Log.e(localClassName, "onActivityResult: path ${path.toString()} ")
}
}
}
}
}
Pass the file URI to the below method to get the file path. This method will create a file object at cache directory of your application and from that location you can easily get Read access to that file.
private fun makeFileCopyInCacheDir(contentUri :Uri) : String? {
try {
val filePathColumn = arrayOf(
//Base File
MediaStore.Files.FileColumns._ID,
MediaStore.Files.FileColumns.TITLE,
MediaStore.Files.FileColumns.DATA,
MediaStore.Files.FileColumns.SIZE,
MediaStore.Files.FileColumns.DATE_ADDED,
MediaStore.Files.FileColumns.DISPLAY_NAME,
//Normal File
MediaStore.MediaColumns.DATA,
MediaStore.MediaColumns.MIME_TYPE,
MediaStore.MediaColumns.DISPLAY_NAME
)
//val contentUri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.provider", File(mediaUrl))
val returnCursor = contentUri.let { contentResolver.query(it, filePathColumn, null, null, null) }
if (returnCursor!=null) {
returnCursor.moveToFirst()
val nameIndex = returnCursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)
val name = returnCursor.getString(nameIndex)
val file = File(cacheDir, name)
val inputStream = contentResolver.openInputStream(contentUri)
val outputStream = FileOutputStream(file)
var read = 0
val maxBufferSize = 1 * 1024 * 1024
val bytesAvailable = inputStream!!.available()
//int bufferSize = 1024;
val bufferSize = Math.min(bytesAvailable, maxBufferSize)
val buffers = ByteArray(bufferSize)
while (inputStream.read(buffers).also { read = it } != -1) {
outputStream.write(buffers, 0, read)
}
inputStream.close()
outputStream.close()
Log.e("File Path", "Path " + file.path)
Log.e("File Size", "Size " + file.length())
return file.absolutePath
}
} catch (ex: Exception) {
Log.e("Exception", ex.message!!)
}
return contentUri.let { UriPathUtils().getRealPathFromURI(this, it).toString() }
}
Note: You can use this method to get file path for both media files (Images, Videos, Audio) and non-media files (Documents and other files) as well. Just need to pass a file Uri.