The following code works on Android 10, but fails on Android 11. Specifically listFiles() returns null. The path does look weird, but it seems to work for other tasks rather than listing files in a dir.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize a list of required permissions to request runtime
val list = listOf<String>(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
)
ActivityCompat.requestPermissions(this, list.toTypedArray(), 123)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.setFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
intent.addFlags(FLAG_GRANT_WRITE_URI_PERMISSION)
startActivityForResult(intent, REQUEST_DOCUMENT_TREE)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_DOCUMENT_TREE) {
if (resultCode == RESULT_OK && data != null) {
data.data?.let { uri ->
val docId = DocumentsContract.getTreeDocumentId(uri)
val dirUri = DocumentsContract.buildDocumentUriUsingTree(uri, docId)
val path = DocumentsContract.findDocumentPath(getContentResolver(), dirUri)?.path
path?.let {
val files = File(path[0].replaceFirst("raw:", "", true))
val ret = files.listFiles()
Log.d("OUR_LOG", "ret is ${ret}")
}
}
}
}
}
}
In manifest I added
...
<uses-permission
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
tools:ignore="ScopedStorage" />
<application
android:requestLegacyExternalStorage="true"
android:preserveLegacyExternalStorage="true"
android:allowBackup="true"
...
How to list files from dirUri on Android 11?
Thank you in advance!
CodePudding user response:
val files = File(path[0].replaceFirst("raw:", "", true)) val ret = files.listFiles()
Of course listFiles returns null as it is a path you have no access to.
You could have used File.exists() and File.canRead() before you tried to list.
Your whole approch is wrong. Do not use the File class to begin with.
DocumentsContract gives you a nice uri for the choosen tree/directory.
Now if you wanna list files build up the children uri for that tree and after that use getContentResolver() to query() that children uri. You will obtain a cursor and an uri for every file then.
CodePudding user response:
The answer of blackapps mentions one solution, but a few points are missing:
Basically you try to be the storage manager with Manifest.permission.MANAGE_EXTERNAL_STORAGE but this permission is not granted like other permissions. You have to ask the user to do this setting manually with an Intent with the action ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION - like mentioned here: https://developer.android.com/training/data-storage/manage-all-files
The permission can be checked with Environment.isExternalStorageManager(). If these permissions are set correctly, you will probably have the file-access working, but there need to be specific reasons why your app should be the Storage Manager.
Otherwise the approach mentioned by blackapps is the correct one.
By the way, the requestLegacyExternalStorage was intended for Android 10 only as a transition and is ignored in Android 11, so the DocumentsContract is the way to go.
