1

First of all I've already read posts like these:

and many others to no avail.

In my DrawerMenu I have an option to share my app which sends 9 screenshots through selected Intent, and even the process is working (I've tested it through Gmail) and the email with the images is sent, the following exception is always shown in logcat (which in my case is repeated 6 times):

Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading com.xxxx.xxx.helpers.file.GenericFileProvider uri content://com.xxx.xxx.provider/cache/testme2.jpg from pid=5417, uid=1000 requires the provider be exported, or grantUriPermission()
    at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:820)
    at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
    at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:674)
    at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:548)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:327)
    at android.os.Binder.execTransactInternal(Binder.java:1154)
    at android.os.Binder.execTransact(Binder.java:1123)

Manifest:

<provider
    android:name=".helpers.file.GenericFileProvider"
    android:authorities="${applicationId}.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths"/>
</provider>

GenericFileProvider:

import androidx.core.content.FileProvider

class GenericFileProvider : FileProvider()

Uris receiver method:

override fun onImageProcessingFinished(ipr: ImageProcessingResult) {
    val uris = ArrayList<Uri>()
    for (file in ipr.screenShotFiles) {
        val uri = FileProvider.getUriForFile(
            this,
            BuildConfig.APPLICATION_ID + ".provider", 
            file
        )
        uris.add(uri)
    }
    TMDrawerMenu.listener = this
    TMDrawerMenu.activity = WeakReference(this)
    TMDrawerMenu.shareFile(uris, "image/jpg")
}

ShareFile:

fun shareFile(uris: ArrayList<Uri>, fileType: String) {
    try {
        val share = Intent(Intent.ACTION_SEND_MULTIPLE)

        share.flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
        share.type = fileType
        val strSubject = "Share Subject"
        share.putExtra(Intent.EXTRA_SUBJECT, strSubject)

        share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
        activity!!.get()!!
            .startActivity(
                Intent.createChooser(
                    share,
                    "Share Text"
                )
            )
    } catch (ex: Exception) {
        ex.logException()
    }
}

provider_paths.xml:

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

I have checked the files exists one by one, so this is not the problem.

As extra information I can say the files are saved into app's cache dir:

val outputPath = this.cacheDir.toString()

Lastly I can add I haven't tested it yet in a real device, for the moment I'm working with emulator APIs 29/30

I cannot understand the reason of the exception when the process works, but I need to find a solution because I have a similar problem in another part of the app.

The error is apparently being thrown in the next file of shareFile:

activity!!.get()!!
    .startActivity(
        Intent.createChooser(
            share,
            "Share Text"
        )
    )

How can I get rid of this exception?

halfer
  • 19,824
  • 17
  • 99
  • 186
Diego Perez
  • 2,188
  • 2
  • 30
  • 58
  • Try skipping `Intent.createChooser()` and see if the Logcat messages go away. If they do, try [using `ShareCompat.IntentBuilder`](https://developer.android.com/reference/kotlin/androidx/core/app/ShareCompat.IntentBuilder) rather than constructing the `Intent` yourself. See https://commonsware.com/blog/2021/01/07/action_send-share-sheet-clipdata.html for more. – CommonsWare Oct 02 '22 at 13:06
  • Thanks for your reply @CommonsWare. I'm trying to test you proposed ShareCompat.IntentBuilder way, but I cannot figure out how to handle multiple files. In your example you use only one single file. – Diego Perez Oct 02 '22 at 13:30
  • Ok, I can confirm that invoking ShareCompat.IntentBuilder.from(this)... the exception goes away, but first, don't know hot to handle multiple files, second, IntentBuilder.from is marked as deprecated. – Diego Perez Oct 02 '22 at 13:34

1 Answers1

1

Yes, indeed only one of all the shared files is readable by the receiving side.

I wonder why this was not reported here before.

The first thing you should add is:

if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
    intent.setClipData(ClipData.newRawUri("myfile", uris.get(0)));

The share sheet can then display some preview thumbnails.

The share sheet will complain with the same exceptions but somehow manages to read all files. Wonder wonder.

If you want the receiver (inclusive GMail) be able to read all files then change to:

                        if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) //  16 )
                        {
                            boolean makeREADpermissionValidForAllFiles = true;
                            if (makeREADpermissionValidForAllFiles)
                            {
                                ClipData clipData = ClipData.newRawUri("myfile0", uriList.get(0));
                                nr = 0;
                                while ( ++nr < uriList.size())
                                    clipData.addItem( new ClipData.Item(uriList.get(nr)));

                                intent.setClipData(clipData);
                            }
                            else
                                intent.setClipData(ClipData.newRawUri("myfile", uriList.get(0)));
                        }
blackapps
  • 8,011
  • 2
  • 11
  • 25
  • Hello and thanks for your reply @blackapps. Can you provide more info regarding if I still have to use Intent(Intent.ACTION_SEND_MULTIPLE) and startActivity(Intent.createChooser( with your approach? Could you please rewrite my shareFile method with your approach? – Diego Perez Oct 02 '22 at 13:43
  • Read! I said you had to ADD something. Nothing more. You should only translate it in Kotlin. – blackapps Oct 02 '22 at 13:48
  • I've finally managed to work with no exception following your approach. I'll markit as correct, but edit it to paste my new shareFile Method. – Diego Perez Oct 02 '22 at 13:50
  • You will not edit my post. – blackapps Oct 02 '22 at 13:51
  • No, I cannot edit it, I'm not allowed to, didn't know, anyway just wanted to paste how the method finally is for anyone else who might be facing the same issue, but in the end now it works, so thanks again. – Diego Perez Oct 02 '22 at 13:54