0

In my Android Unity plugin, I want to provide access to the screenshot that unity made, so provide access to Application.persistentDataPath, which is the files folder of the app per documentation...

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path name="files" path="."/>
</paths>

I want also to read / write external storage. But as soon as I add...

<!-- For access `DICM/Camera` -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

...to the manifest, Unity adds the screenshots to the external storage, so FileProvider cannot provide files from there.

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/<PACKAGE_NAME>/files/<FILE_NAME>

How can I still provide files from app files directory wherever it resides?

Geri Borbás
  • 15,810
  • 18
  • 109
  • 172

1 Answers1

2

I usually see that message coming from getUriForFile(), when you pass in a File that does not match your configuration.

In your provider metadata, you are only supporting getFilesDir() (and locations underneath it) as places that you are willing to serve from. However, the File object that you passed into getUriForFile() appears to be built from getExternalFilesDir(), which is not the same thing.

Unfortunately, FileProvider does not provide direct access to getExternalFilesDir(). <external-path> comes close, and the documentation claims that it points to getExternalFilesDir(). That is a documentation bug; <external-path> really points to the root of external storage (i.e., Environment.getExternalStorageDirectory()).

Your options are:

  1. Stick to FileProvider just for your internal storage files, and use file:// Uri values for the files on external storage

  2. Work out an <external-path> value to add to your provider metadata that points to your particular portion of external storage, based on your application ID

  3. Use my StreamProvider, which adds <external-files-path> as an option

  4. Roll your own ContentProvider to handle this, rather than using either FileProvider or StreamProvider

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks! Awesome answer. I started Android development on the weekend, but I already familiar with the name @CommonsWare :D – Geri Borbás Mar 08 '16 at 16:15
  • To the issue: If I remove the `` entry from manifest, the fileprovider works fine, without any further change in the entire project. The file (a screenshot made by Unity actually) gets provided fine form the applications `files` directory. I really would like to understand what is happening there. – Geri Borbás Mar 08 '16 at 16:17
  • @Geri: Sorry, but I cannot explain your symptoms given what you have shown here. Could you edit your question to show the full stack trace, along with the code that is triggering the stack trace? – CommonsWare Mar 08 '16 at 16:20
  • `FileProvider` somehow comes external as soon as I add `` to external storage. Maybe it is a Unity issue, as I use [**`Application.persistentDataPath`**](http://docs.unity3d.com/ScriptReference/Application-persistentDataPath.html) API to retreive a path that I pass on to `FileProvider`. My next idea is that it points to the external storage once I claim that permission. I'll look after on Unity forums. – Geri Borbás Mar 08 '16 at 16:25
  • I'm about to go option 2., as it follows up the changed behaviour. But it will break as the plugin gets built into another app with another package name. :( – Geri Borbás Mar 08 '16 at 16:42
  • Will changed application package name break `FileProvider` alltogether? It's `android:authorities` set to the package name of the library it resides. – Geri Borbás Mar 08 '16 at 16:48
  • 1
    @Geri: While usually `authorities` gets tied to the application ID, that's not a requirement. It just has to be unique. The path for where `getExternalFilesDir()` puts its files is strongly tied to the application ID. – CommonsWare Mar 08 '16 at 16:56
  • I have to use external storage (media scan into Gallery not working otherwise). If I do so, Unity puts the screenshot to an external storage folder with application package name, so being a plugin, I cannot go with hardcoded . But email attachment permissions works with FileProvider the best. Probably the solution will be to maintain a custom **folder on the External storage for the plugin (!)**, and not for the app that use it. – Geri Borbás Mar 08 '16 at 20:48
  • Having all the things I mentioned before, option 1, 3 and 4 can't be a resolution, as I don't know future application package names at the time I build the plugin. Unless there is any option to manipulate `FileProvider` paths at runtime (?), I have no other options left beside custom external plugin folder. – Geri Borbás Mar 08 '16 at 20:55
  • 1
    @Geri: Well, I don't know much about Unity plugins. That being said, there's little question that `FileProvider` (and my `StreamProvider` by extension) are designed for use by apps, not libraries. A `ContentProvider` might still work, given a proper `build.gradle` file and some manifest merger work to use placeholders for the authority name, so it could vary by app. – CommonsWare Mar 08 '16 at 21:01
  • Yap, I modded the question, and probably go on with the "folder created at runtime" method, and `` you've suggested. Thanks for the discusson! – Geri Borbás Mar 08 '16 at 21:04