5

I have been trying to solve a problen with sharing image from external storage directory. it works in most of the devices, but It is not working in others. I have: 1. Added a class extending FileProvider:

public class GenericFileProvider extends FileProvider {}
  1. Added a FileProvider tag in AndroidManifest.xml under tag:

 <provider
            android:name=".Utils.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>
  1. created a provider_paths.xml file in res/xml folder:

 <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        tools:ignore="MissingDefaultResource">
        <external-path name="external_files" path="."/>
    </paths>

and my function for sharing image is down below:

 private fun shareInEmail() {
    val filename = "Avoir+$timeStamp.jpg"
    val filelocation = File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename)
    val path : Uri = FileProvider.getUriForFile(
        requireContext(),
        context!!.applicationContext.packageName.toString() + ".provider",
        filelocation
    )

    context!!.grantUriPermission(
        "com.ideasfactory.mjcprojet",
        path,
        Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    )

    val emailIntent = Intent(Intent.ACTION_SEND)
    emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
    // set the type to 'email'
    emailIntent.type = "vnd.android.cursor.dir/email"
    emailIntent.setType("text/plain")
    emailIntent.type = "application/jpg"
    val to = arrayOf("contact@mjclambres.fr")
    emailIntent.putExtra(Intent.EXTRA_EMAIL, to)
    emailIntent.putExtra(Intent.EXTRA_BCC, arrayOf<String>())
    // the attachment
    emailIntent.putExtra(Intent.EXTRA_STREAM, path)
    // the mail subject
    emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Transaction")
    emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    startActivity(Intent.createChooser(emailIntent, "Transaction"))


}

All the previous steps reference to this answer: [android.os.FileUriExposedException: file:///storage/emulated/0/test.txt exposed beyond app through Intent.getData()

I don’t know what i am missing? My log says:

E/DatabaseUtils: Writing exception to parcel
java.lang.SecurityException: Permission Denial: reading com.ideasfactory.mjcprojet.Utils.GenericFileProvider uri content://com.ideasfactory.mjcprojet.provider/external_files/Avoir%2B2020-06-11%2011%3A08.jpg from pid=12602, uid=1000 requires the provider be exported, or grantUriPermission()
    at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729)
    at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602)
    at android.content.ContentProvider$Transport.enforceFilePermission(ContentProvider.java:593)
    at android.content.ContentProvider$Transport.openTypedAssetFile(ContentProvider.java:507)
    at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:307)
    at android.os.Binder.execTransactInternal(Binder.java:1021)
    at android.os.Binder.execTransact(Binder.java:994)

I will appreciated all your help

5 Answers5

7

The reason a security exception is thrown is because the android system is trying to access the shared resource to give the user a preview of what would be shared.

The best approach is to use the Intent.setClipData to share the resource in addition to Intent.putExtra(Intent.EXTRA_STREAM, fileUri)

Your code should look like this:

...
// the attachment
emailIntent.setClipData(ClipData.newRawUri("", path))
emailIntent.putExtra(Intent.EXTRA_STREAM, path)
...

For a additional reference on this, please take a look at the FileProvider class as well.

Dimitar Darazhanski
  • 2,188
  • 20
  • 22
3

Hey there i have faced the same issue and after alot research i found out that the problem was in provider_paths.xml.And i got different paths for many different possibilities. Change your provider_paths.xml with the following code:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

  <cache-path name="cache" path="." />
  <external-path name="external" path="." />

  <root-path name="root" path="." />
  <files-path name="my_images" path="/" />
  <files-path name="my_images" path="myfile/"/>
  <files-path name="files" path="." />

  <external-path name="external_files" path="." />
  <external-path name="images" path="Pictures" />
  <external-path name="my_images" path="." />
  <external-files-path name="images" path="Pictures"/>
  <external-files-path name="camera_image" path="Pictures/"/>
  <external-files-path name="external_files" path="." />
  <external-files-path name="my_images" path="my_images" />

  <external-cache-path name="external_cache" path="." />
</paths>
Aashit Shah
  • 578
  • 3
  • 9
  • 2
    Thanks a lot for your help. I'm new in Android, I just want to know hoy do I acces to `my_images` name and `myfile` path. I'm using `File(Environment.getExternalStorageDirectory().getAbsolutePath(), filename)` in my.Kotlin class but i don't know how to do that in xml file. thanks again for any help! – Lady Geraldine Villamil Guerre Jun 11 '20 at 12:49
2

Try this one

Intent intentShareFile = new Intent(Intent.ACTION_SEND);

    intentShareFile.setType(URLConnection.guessContentTypeFromName(file.getName()));

    Uri uri = FileProvider.getUriForFile(this, "yourpackage", file);

    intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);

    List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intentShareFile, PackageManager.MATCH_DEFAULT_ONLY);
    for (ResolveInfo resolveInfo : resInfoList) {
        String packageName = resolveInfo.activityInfo.packageName;
        grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
  • 1
    Querying all packages starting from Android 11 requires special permission, called "QUERY_ALL_PACKAGES". And it is subject to approval. [More Details](https://developer.android.com/training/package-visibility#:~:text=In%20the%20rare%20cases%20where%20the%20%3Cqueries%3E%20element%20doesn%27t%20provide%20adequate%20package%20visibility%2C%20you%20can%20use%20the%20QUERY_ALL_PACKAGES%20permission.%20If%20you%20publish%20your%20app%20on%20Google%20Play%2C%20your%20app%27s%20use%20of%20this%20permission%20is%20subject%20to%20approval%20based%20on%20an%20upcoming%20policy.) – Waqas Younis Feb 19 '22 at 09:29
1
  Intent intentShareFile = new Intent(Intent.ACTION_SEND);

         intentShareFile.setType("application/pdf");

         Uri uri = FileProvider.getUriForFile(this,  getApplicationContext().getPackageName() + ".provider", pdf);
         intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);

         List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intentShareFile, PackageManager.MATCH_DEFAULT_ONLY);
         for (ResolveInfo resolveInfo : resInfoList) {
             String packageName = resolveInfo.activityInfo.packageName;
             grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
         }
         startActivity(Intent.createChooser(intentShareFile, "Share PDF using.."));
  • 2
    Hi, welcome to Stack Overflow. Please refrain from code-only answers. In this case, you might explain why the OP received the error and how this solves it. – Jeremy Caney Jul 01 '21 at 01:30
1

If you have single uri to share use this method,

val shareCompat = ShareCompat
                .IntentBuilder(Your activity)
                .setType("mimeType")
                .addStream(uri)
                .setChooserTitle("Share")
                .startChooser()

If you have multiple files to share add all files to one by one like below

uris.forEach { shareCompat.addStream(it) }

after that call shareCompat.startChooser()

kalandar
  • 793
  • 6
  • 13