0

Setup: I get an URI of the directory in shared storage using procedure described here: Access documents and other files from shared storage / Grant access to a directory's contents.

Question: How to create a file inside this directory?

More details:

The guide mentioned above explains, how to list files in this directory, open some file for reading or modification and also how to delete it. All this using ContentResolver. (Edit: actually guide does not explain it also, but just how to delete or modify files obtained interactively with ACTION_OPEN_DOCUMENT). But I cannot see, how to create some file inside this directory.

Two candidates, which I can imagine, could help me with the job are: ContentResolver.insert() or ParcelFileDescriptor(file, MODE_CREATE), but I cannot figure out how I could use them in this situation.

I should also mention, that I know about the possibility to create the file, when user interactively selects the file name and location, but it is not good solution for me. In my context I need to create several files inside one folder. All files should be accessible/modifiable/deletable by user from outside of my application, thus "shared storage". And the files are named and located within their "main" directory according to some conventions, so I don't want the user to explicitly provide me the name for each file, but just the location of "main directory".

Dmitrii Semikin
  • 2,134
  • 2
  • 20
  • 25
  • `how to create file in the directory opened with ACTION_OPEN_DOCUMENT_TREE` That is a pretty basic Storage Access Framework task and has been published many times. A little googling should give you so many examples. Please post your code if it does not work. – blackapps Nov 16 '20 at 15:02
  • 1
    Simple googling gave immediately: https://stackoverflow.com/questions/61118918/create-new-file-in-the-directory-returned-by-intent-action-open-document-tree – blackapps Nov 16 '20 at 15:08
  • @blackapps Thank you for the link. It helped. The thing, which I was missing is `DocumentFile.fromTreeUri()` (or more general DocumentContract). I wonder, why it is not mentioned in the official guide... By the way, just out of curiosity, what did you search for in google? My trials did not give me the link to this answer... – Dmitrii Semikin Nov 16 '20 at 15:38
  • ACTION_OPEN_DOCUMENT_TREE create file – blackapps Nov 16 '20 at 16:29
  • @blackapps: hm... this one delivers link to another post :). But it also mentions `DocumentFile`. So, my bad... – Dmitrii Semikin Nov 16 '20 at 16:52

1 Answers1

0

Point 1: blackapps is right in the comments to the original question and this question is duplicate of e.g. those:

Create new file in the directory returned by Intent.ACTION_OPEN_DOCUMENT_TREE

or

Write file to directory using Intent.ACTION_OPEN_DOCUMENT_TREE

Point 2 (Quick answer): One should use in this case DocumentFile.fromTreeUri(this, uri) with the URI returned from the Intent.ACTION_OPEN_DOCUMENT_TREE. This worked for me out of the box.

Point 3: One thing was still confusing for me:

If I tried to use something like this (from the original guide, which I used) - to query metadata of the URI:

Cursor cursor = getContentResolver().query(uri, null, null, null, null, null);

Or if I tried to call something like this, hoping to create new document without use of DocumentFile, but instead using directly DocumentsContract:

Uri createdDocumentUri = DocumentsContract.createDocument(
    getContentResolver(),
    uri,
    "text/plain",
    "someDocument.txt");

Then I always got an exception. In the first case it was:

java.lang.UnsupportedOperationException: Unsupported Uri content://com.android.externalstorage.documents...

And in the second case:

java.lang.IllegalArgumentException: Invalid URI: content://com.android.externalstorage.documents...

Different exceptions, but in both cases the message is "Unupported URI".

For a while I could not understand, why is it the case. But when I looked inside DocumentFile.fromTreeUri() and discovered, that instead of using URI returned by Intent.ACTION_OPEN_DOCUMENT_TREE directly, one needs to process it in a way like this (the code is taken from DocumentFile.fromTreeUri()):

    String documentId = DocumentsContract.getTreeDocumentId(uri);
    Uri newUri = DocumentsContract.buildDocumentUriUsingTree(uri, documentId);

The documentation of DocumentsContract.buildDocumentUriUsingTree(), explains little bit, what is going on:

... However, instead of directly accessing the target document, the returned URI will leverage access granted through a subtree URI, typically returned by Intent#ACTION_OPEN_DOCUMENT_TREE. The target document must be a descendant (child, grandchild, etc) of the subtree.

This is typically used to access documents under a user-selected directory tree, since it doesn't require the user to separately confirm each new document access.

I hope, that this answer will help someone to save a bit of confusion, which I had, while I was trying to clarify this topic...

Dmitrii Semikin
  • 2,134
  • 2
  • 20
  • 25