3

I'm trying to pass an image that resides in the res/raw directory of my app along with a share intent.

I followed the process described in the FileProvider docs, and here's my code:

AndroidManifest.xml

<application ...>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.myapp.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">

        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
    </provider>
</application>

res/xml/paths.xml

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="shared" path="./"/>
</paths>

The code in my activity:

String shareToPackage = ...

File imageFile = new File(context.getFilesDir().getPath() + "/image");
if (!imageFile.exists()) { // image isn't in the files dir, copy from the res/raw
    final InputStream inputStream = context.getResources().openRawResource(R.raw.my_image);
    final FileOutputStream outputStream = context.openFileOutput("image", Context.MODE_PRIVATE);

    byte buf[] = new byte[1024];
    int len;
    while ((len = inputStream.read(buf)) > 0) {
        outputStream.write(buf, 0, len);
    }

    outputStream.close();
    inputStream.close();

    imageFile = new File(context.getFilesDir().getPath() + "/image");
}

if (!imageFile.exists()) {
    throw new IOException("couldn't find file");
}

final Uri uri = Uri.fromFile(imageFile);
context.grantUriPermission(shareToPackage, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/png");
intent.putExtra(Intent.EXTRA_TEXT, "here's the image");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.setPackage(shareToPackage);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);

The above does not work as the file that I get in the other app isn't accessible:

java.io.FileNotFoundException: FILE_PATH: open failed: EACCES (Permission denied)

Any idea what I'm doing wrong here?
Thanks.

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • Did you ever solve this issue - facing the exact same... – brandall Sep 11 '16 at 08:23
  • 1
    @brandall Nope. I abandoned it as too much time was wasted on it. If you do find a solution please answer it here so that others in the future won't need to waste much time as well. GOOD LUCK! – Nitzan Tomer Sep 11 '16 at 08:30
  • 2
    Unbelievable... 6 hours down the toilet already on this, for something that should be so simple. If I solve it, I'll post back..... – brandall Sep 11 '16 at 10:29

2 Answers2

3

Get rid of the path attribute in <files-path>, as it is not needed here, since you are serving everything from getFilesDir().

Do not use string concatenation when creating File objects. Replace:

new File(context.getFilesDir().getPath() + "/image.png");

with:

new File(context.getFilesDir().getPath(), "image.png");

Most importantly, do not use Uri.fromFile(). Use FileProvider.getUriForFile(). As it stands, you are going through all this work to set up FileProvider, then you do not use the FileProvider for making the content available to the other app.

Or, get rid of all of this, and use my StreamProvider, which can serve a raw resource directly.

Or, write your own ContentProvider that serves the raw resource directly.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks. I changed everything that you suggested, and the app who gets the intent still doesn't have access to the file, but now it says that the file doesn't exist instead of saying permission denied. The file is there for sure, as I check for that before I send the intent, so what's going on here? Your `StreamProvider` sounds interesting, but I'd like to first fix this current version and then I'll try your out. – Nitzan Tomer Aug 03 '16 at 12:16
  • @NitzanTomer: Before `outputStream.close();`, add `outputStream.flush();` and `outputStream.getFD().sync();`, to ensure all bytes are written to disk. Also, comment out the `EXTRA_TEXT`, as `ACTION_SEND` supports either `EXTRA_TEXT` or `EXTRA_STREAM`, not necessarily both. You might also log the `Uri` that you get from `FileProvider` to see if it makes sense. – CommonsWare Aug 03 '16 at 12:25
  • Added the `flush` and `sync`, removed the `EXTRA_TEXT` but still the file doesn't exist. The uri seems ok to me: `content://com.myapp.fileprovider/shared/image`, but I'm no expert so I might be wrong. – Nitzan Tomer Aug 03 '16 at 12:36
  • @NitzanTomer: I agree that the `Uri` looks correct. It may be a bug in the other app. You can try blending in [my `LegacyCompatCursorWrapper`](https://github.com/commonsguy/cwac-provider#usage-legacycompatcursorwrapper), which helps with some client apps. [This sample app](https://github.com/commonsguy/cw-omnibus/tree/master/ContentProvider/V4FileProvider) demonstrates its use with `FileProvider`. – CommonsWare Aug 03 '16 at 12:47
  • No, the other app is my own (because I'm testing this uri attachment), and I'm not querying or anything, just try to open the file based on the received uri. I also tried your `StreamProvider` and still on the receiving app the file does not exist. I must be doing something wrong, but can't figure out what – Nitzan Tomer Aug 03 '16 at 13:59
  • @NitzanTomer: "just try to open the file based on the received uri" -- then perhaps there is a bug in your client app. After all, you cannot "open the file", since it is not a file. See https://commonsware.com/blog/2016/03/15/how-consume-content-uri.html for more about how to consume content from a `Uri`. – CommonsWare Aug 03 '16 at 14:00
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/119020/discussion-between-nitzan-tomer-and-commonsware). – Nitzan Tomer Aug 03 '16 at 14:35
0

@nitzan-tomer, see https://stackoverflow.com/a/33031091/966789

What Are Runtime Permissions?

With Android 6.0 Marshmallow, Google introduced a new permission model that allows users to better understand why an application may be requesting specific permissions. Rather than the user blindly accepting all permissions at install time, the user is now prompted to accept permissions as they become necessary during application use.

Alexander Lubyagin
  • 1,346
  • 1
  • 16
  • 30