Search code examples
androidkotlinstorage-access-framework

Android Storage Access Framework (SAF): How to read file in internal file browser?


due to restrictions imposed by Google, I no longer have access to read all files in Android API 33+. I had presumed I could easily use Storage Access Framework (SAF) to list files within a directory selected by the user via "ACTION_OPEN_DOCUMENT_TREE".

Here's how I attempted it:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
m_OpenDocumentTreeLauncher.launch(intent)

I can access the directory and list all files within it into my internal browser like so:

val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(rootUri, DocumentsContract.getTreeDocumentId(rootUri))
val contentResolver = requireContext().contentResolver

val projection = arrayOf(
    DocumentsContract.Document.COLUMN_DOCUMENT_ID,
    DocumentsContract.Document.COLUMN_DISPLAY_NAME,
    DocumentsContract.Document.COLUMN_SIZE,
    DocumentsContract.Document.COLUMN_MIME_TYPE,
)

val cursor = contentResolver.query(childrenUri, projection, null, null, null)
if (cursor != null)
{
   try
   {
       while (cursor.moveToNext())
       {
           val docId = cursor.getString(0)
           val name = cursor.getString(1)
           val size = cursor.getLong(2)
           val mimeType = cursor.getString(3)

           fileList.add(rootUri.authority.toString(), docId, name, size, mimeType)
        }
    }
    finally {
        cursor.close()
    }
}

RootUri is the Uri of the selected directory via ACTION_OPEN_DOCUMENT_TREE. docId is the ID of the selected file. And loading this file something like this:

        val documentUri = DocumentsContract.buildDocumentUri(fileList.authority, fileList.docId)
        val contentResolver = requireContext().contentResolver
        val inputStream = contentResolver.openInputStream(documentUri) 

The Uri for the file I get in this example is: content://com.android.externalstorage.documents/document/9C35-6BCD%3ATest%2FTestFile.txt

So far, so good. Now, when I attempt to click on this file from to load its content into a FileStream, I encounter an issue. I consistently receive the error:

"Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/document/9C35-6BCD%3ATest%2FTestFile.txt from pid=22147, uid=10488 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs"

I had assumed that I would have access to files in this directory selected by the user via "ACTION_OPEN_DOCUMENT_TREE". Is this not the case? How can I open/read a file in this folder without resorting to "ACTION_OPEN_DOCUMENT"? I prefer using my internal browser for its ease and speed, as well as for a better user experience. Is this functionality no longer possible, or is there an alternative approach? Thank you!


Solution

  • val documentUri = DocumentsContract.buildDocumentUri(fileList.authority, ....

    You cannot use that function.

    Instead use

    val childUri = DocumentsContract.buildDocumentUriUsingTree(rootUri, docId)
    

    And see that the obtained content scheme is completely different.