3

I am trying to share a file (any type of file) from my app with another app in the phone (via the Share menu, for example), or preview it in the phone's default app, but I need to do this without creating a copy of the file in a temp or cache folder to then use the path to create an intent, as it is commonly done.

The difference is that I already have the file in the phone, but it's stored in a custom file system of sorts (this is the app we are working on, and among other things it encrypts the file, which is why the file is not in a regular folder in the phone). I can easily get the file's bytes, create a reader, etc; but so far I have only found the usual way to do what I need: create a temporary file, write the bytes to it, then create an intent with the path to this file, and so on.

Am I missing something?

Is there a way to pass, instead of the path to the file, other kind of abstraction that tells the other app or to Android, to use it to get the file (the bytes?) from a reader or something similar?

1 Answers1

2

I can easily get the file's bytes, create a reader, etc; but so far I have only found the usual way to do what I need: create a temporary file, write the bytes to it, then create an intent with the path to this file, and so on.

That has not worked in several years, if by "create an intent with the path to this file" you mean something like using Uri.fromFile().

Is there a way to pass, instead of the path to the file, other kind of abstraction that tells the other app or to Android, to use it to get the file (the bytes?) from a reader or something similar?

A Uri is not a file. The Uri that you supply via EXTRA_STREAM on an ACTION_SEND Intent does not have to point to a file. In fact, it's not supposed to point directly to a file.

It is supposed to point to a ContentProvider.

Now, a common ContentProvider implementation for ACTION_SEND is FileProvider, which is a ContentProvider that knows how to stream the contents of files on the filesystem. However, you do not have to use that — you can create your own ContentProvider that streams content from other sources.

You can download for free an older edition of one of my books. It has a lot of material on ContentProvider, including ~50 pages in two chapters focused on how to implement custom providers. You would be using the streaming-style API more than the database-style API. Off the cuff, the Pipe example is probably the closest to what you would be doing.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks for sharing your book @CommonsWare, and for the clarification on `ContentProviders`; I will read and try using your points. Your answer was really helpful. – Jorge Pabón Jun 11 '21 at 13:44
  • Is it correct to say that the Pipe example creates a linear non seekable stream of bytes for the receiver? If so, is there a way to provide a seekable `ParcelFileDescriptor` or is that not supported by the API? – Peter Jankuliak Feb 10 '22 at 10:14
  • 1
    @PeterJankuliak: "Is it correct to say that the Pipe example creates a linear non seekable stream of bytes for the receiver?" -- that is a nice description! "is there a way to provide a seekable ParcelFileDescriptor or is that not supported by the API?" -- I never found a solution for that, after years of searching. It is possible that something was added for this in the past few releases, as I stopped worrying about the problem and may have missed something. – CommonsWare Feb 10 '22 at 12:44
  • 1
    @CommonsWare given your activity on this topic, I think you may be interested in hearing that we got the proxy file descriptor approach working as well. The code might still need some polishing, but it works. It can be found [here](https://github.com/equalitie/ouisync-plugin/blob/c4955bbe4355e36beea1e4296da7001f2c8c5b24/android/src/main/kotlin/ie/equalit/ouisync_plugin/PipeProvider.kt#L53-L72). Just to recapitulate: this approach enables the apps that consume the content to get the file size without reading it all as well as seeking through the file (useful e.g. when watching big videos). – Peter Jankuliak Feb 28 '22 at 11:00