How to use support FileProvider for sharing content to other apps? How to use support FileProvider for sharing content to other apps? android android

How to use support FileProvider for sharing content to other apps?


Using FileProvider from support library you have to manually grant and revoke permissions(at runtime) for other apps to read specific Uri. Use Context.grantUriPermission and Context.revokeUriPermission methods.

For example:

//grant permision for app with package "packegeName", eg. before starting other app via intentcontext.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);//revoke permisionscontext.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

As a last resort, if you can't provide package name you can grant the permission to all apps that can handle specific intent:

//grant permisions for all apps that can handle given intentIntent intent = new Intent();intent.setAction(Intent.ACTION_SEND);...List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);for (ResolveInfo resolveInfo : resInfoList) {    String packageName = resolveInfo.activityInfo.packageName;    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);}

Alternative method according to the documentation:

  • Put the content URI in an Intent by calling setData().
  • Next, call the method Intent.setFlags() with either FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION or both.
  • Finally, send the Intent to another app. Most often, you do this by calling setResult().

    Permissions granted in an Intent remain in effect while the stack of the receiving Activity is active. When the stack finishes, the
    permissions are automatically removed. Permissions granted to one
    Activity in a client app are automatically extended to other
    components of that app.

Btw. if you need to, you can copy source of FileProvider and change attachInfo method to prevent provider from checking if it is exported.


Fully working code sample how to share file from inner app folder. Tested on Android 7 and Android 5.

AndroidManifest.xml

</application>   ....    <provider        android:name="androidx.core.content.FileProvider"        android:authorities="android.getqardio.com.gmslocationtest"        android:exported="false"        android:grantUriPermissions="true">        <meta-data            android:name="android.support.FILE_PROVIDER_PATHS"            android:resource="@xml/provider_paths"/>    </provider></application>

xml/provider_paths

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

Code itself

    File imagePath = new File(getFilesDir(), "external_files");    imagePath.mkdir();    File imageFile = new File(imagePath.getPath(), "test.jpg");    // Write data in your file    Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);    Intent intent = ShareCompat.IntentBuilder.from(this)                .setStream(uri) // uri from FileProvider                .setType("text/html")                .getIntent()                .setAction(Intent.ACTION_VIEW) //Change if needed                .setDataAndType(uri, "image/*")                .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);   startActivity(intent);


This solution works for me since OS 4.4. To make it work on all devices I added a workaround for older devices. This ensures that always the safest solution is used.

Manifest.xml:

    <provider        android:name="androidx.core.content.FileProvider"        android:authorities="com.package.name.fileprovider"        android:exported="false"        android:grantUriPermissions="true">        <meta-data            android:name="android.support.FILE_PROVIDER_PATHS"            android:resource="@xml/file_paths" />    </provider>

file_paths.xml:

<paths>    <files-path name="app_directory" path="directory/"/></paths>

Java:

public static void sendFile(Context context) {    Intent intent = new Intent(Intent.ACTION_SEND);    intent.setType("text/plain");    String dirpath = context.getFilesDir() + File.separator + "directory";    File file = new File(dirpath + File.separator + "file.txt");    Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);    intent.putExtra(Intent.EXTRA_STREAM, uri);    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);    // Workaround for Android bug.    // grantUriPermission also needed for KITKAT,    // see https://code.google.com/p/android/issues/detail?id=76683    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {        List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);        for (ResolveInfo resolveInfo : resInfoList) {            String packageName = resolveInfo.activityInfo.packageName;            context.grantUriPermission(packageName, attachmentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);        }    }    if (intent.resolveActivity(context.getPackageManager()) != null) {        context.startActivity(intent);    }}public static void revokeFileReadPermission(Context context) {    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {        String dirpath = context.getFilesDir() + File.separator + "directory";        File file = new File(dirpath + File.separator + "file.txt");        Uri uri = FileProvider.getUriForFile(context, "com.package.name.fileprovider", file);        context.revokeUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);    }}

The permission is revoked with revokeFileReadPermission() in the onResume and onDestroy() methods of the Fragment or the Activity.