1

After about a week of pulling my hair out, I'm finally done and ready to ask for some help.

Basically in my app I use the Intent below to create a new PDF, which is done via Storage Access Framework.

val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "application/pdf"
intent.putExtra(Intent.EXTRA_TITLE, title)
startActivityForResult(intent, 1234)

After that I get the Uri on the onActivityResult() method, like so:

uri = dataIntent.data
if (uri != null) {
    val takeFlags = data.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    contentResolver.takePersistableUriPermission(uri, takeFlags)
    generatePdf(uri)
}

PDF generation is ok, the problem comes when I need to call ACTION_VIEW for the user to see the generated file or to share the file using ACTION_SEND.

Example of ACTION_VIEW usage (Yes, I'm using both Kotlin and Java):

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, mimeType);
startActivity(intent);

I can't for the life of me figure out how to get an Uri that another app can use.

What I tried so far:

  • This answer, but the following exception is thrown: java.lang.IllegalArgumentException: column '_data' does not exist. Available columns: [_display_name, _size]
  • DocumentFile, using DocumentFile.fromFile(file), which turns the Uri from content://com.myapp.provider/root/document/primary:folder-created-by-the-user/generated-pdf.pdf to file:///root/document/primary:folder-created-by-the-user/generated-pdf.pdf, and still no app can open it
  • Many many other things that I can't even remember anymore

If someone could shed some light on this issue would be truly appreciated.

2 Answers2

2

In principle use the same uri as obtained at creating the file. But ...you cannot grant a read uri permission on that uri. You got it. But you cannot forward such a permission to a viewer of your document.

Instead you should implement a ContentProvider. Then you can serve the content of your file.

blackapps
  • 8,011
  • 2
  • 11
  • 25
  • 1
    Great you found a solution yourself. You used ACTION_CREATE_DOCUMENT to let te user create a document. Now you asked to serve that document (with the obtained content scheme). So you need a provider. But you are now talking about files in getFilesDir(). That is a completely different story because what does it have to to with the original file? Files in getFilesDIr can be served with a simple FileProvider. Well only one by one. – blackapps Sep 27 '19 at 22:54
  • I changed my approach so I'm not using ACTION_CREATE_DOCUMENT anymore. What I ended up with was a more ellegant solution in my opinion, because there's no need for the user to select a folder and/or a file name, and all the files I create are stored in my app's provider. – Lucas Couto Sep 29 '19 at 14:34
  • @blackapps I agree with you, the solution that Lucas has come to, is for files created in the private storrage. Would you please give a code solution to open shared files (taken from ACTION_CREATE_DOCUMENT) via Action_View? I have also posted a separate question for it [here](https://stackoverflow.com/questions/68168717/how-to-open-a-file-uri-that-is-taken-from-action-create-document-via-intent-acti). – SoSa Jun 29 '21 at 11:16
1

Like blackapps said in his response, what I had to do was implement a ContentProvider, more specifically a DocumentProvider.

Following this link and this link is what finally did the trick. I implemented a CustomDocumentProvider that exposes a folder inside my app's private files (context.getFilesDir().getAbsolutePath() + "/folderToExpose"), after that all files created in this folder were exposed to other apps and I could use ACTION_VIEW and ACTION_SEND normally.

If someone happens to come across this issue, just make sure that the folder you want to expose doesn't contain any files that are crucial to your app, like database files, since users will have full access to all of its contents. And if it is a new folder, make sure to create it by calling mkdirs().