Get a DocumentFile that is a child of a document tree without using findFiles()

Viewed 1549

I have a tree URI that I got from ACTION_OPEN_DOCUMENT_TREE, how can I get a DocumentFile that is a child of this tree without using findFiles()? Given that I know how to get its documentId, its absolute path or its URI. I need the permissions associated with the document tree.

This is what I have right now:

DocumentFile rootTree = DocumentFile.fromTreeUri(context, rootTreeUri);
DocumentFile docFile = rootTree.findFile(fileName);

It returns docFile as a TreeDocumentFile, child of my root. I have write permission and can use createFile() on it since it is under the document tree from ACTION_OPEN_DOCUMENT_TREE.

So it works but findFile() is really slow.

If I try to use DocumentsContract like so:

// I know how to build the documentId from the file's absolute path
Uri uri = DocumentsContract.buildTreeDocumentUri(rootTreeUri.getAuthority(), documentId);
DocumentFile docFile = DocumentFile.fromTreeUri(context, uri);
// Result: "content://com.android.externalstorage.documents/tree/1A0E-0E2E:myChildDirectory/document/1A0E-0E2E:myChildDirectory"

It returns a new TreeDocumentFile rooted at docFile, not docFile as a child of my original document tree (root). So I don't have write permission on this tree.

And if I try like so:

Uri docUri = DocumentsContract.buildDocumentUriUsingTree(rootTreeUri, documentId);
// Result: "content://com.android.externalstorage.documents/tree/1A0E-0E2E:/document/1A0E-0E2E:myChildDirectory"

I get a URI that actually looks like what I want, but it's a URI, not a DocumentFile.

If I do the same as above but build a DocumentFile from this uri with fromTreeUri():

Uri docUri = DocumentsContract.buildDocumentUriUsingTree(rootTreeUri, documentId);
DocumentFile docFile = DocumentFile.fromTreeUri(context, docUri);
// Result: "content://com.android.externalstorage.documents/tree/1A0E-0E2E:/document/1A0E-0E2E:"

I get the original tree DocumentFile, not a DocumentFile representing the child.

2 Answers

There is possible solution:

https://www.reddit.com/r/androiddev/comments/orytnx/fixing_treedocumentfilefindfile_lousy_performance/

Below is citation from above link:

  @Nullable
static public DocumentFile findFile(@NonNull context, @NonNull DocumentFile documentFile, @NonNull String displayName) {


    if(!(documentFile instanceof TreeDocumentFile)) {
        return documentFile.findFile(displayName);
    }

    final ContentResolver resolver = context.getContentResolver();
    final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(documentFile.getUri(),
            DocumentsContract.getDocumentId(documentFile.getUri()));

    Cursor c = null;
    try {
        c = resolver.query(childrenUri, new String[] {
                DocumentsContract.Document.COLUMN_DOCUMENT_ID,
                DocumentsContract.Document.COLUMN_DISPLAY_NAME,
        }, null, null, null);

        if(c != null) {
            while (c.moveToNext()) {
                if (displayName.equals(c.getString(1))) {
                    return new TreeDocumentFile(documentFile,
                            context,
                            DocumentsContract.buildDocumentUriUsingTree(documentFile.getUri(), c.getString(0)));
                }
            }
        }
    } catch (Exception e) {
        Log.w(TAG, "query failed: " + e);
    } finally {                 
        IOUtils.closeQuietly(c);
    }

    return null;
}

Note that for accessing package protected class TreeDocumentFile, you will have to put function above in a helper class in package androidx.documentfile.provider. After this, replace all calls of DocumentFile#findFile by this replacement or a Kotlin adaptation of it.

Related