Share private files with other apps: FileProvider

Sharing a private file with other apps, it’s a challenge every Android developer will have to tackle someday. You will spend hours on this, probably give up, and then finally end up on this blog to find out how to implement this feature.

The Android support library v4 contains a class called FileProvider, it’s a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri. A content URI allows you to grant read and write access using temporary access permissions.

The first thing you should do is add a new content provider to your AndroidManifest file:

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

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

        </provider>
        ...
    </application>
</manifest>

A FileProvider can only generate a content URI for files in directories that you specify beforehand. To specify a directory, specify its storage area and path in XML (save the file as res/xml/fileprovider.xml).

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

In this example we use the <cache-path />  tag for the cache subdirectory of your app’s internal storage area. Other possibilities are <files-path /> (represents the files/ subdirectory of your app’s internal storage area) and <external-path /> (represents files in the root of your app’s external storage area).

Now, the only thing left to do is to share the file with other applications:

// concatenate the internal cache folder with the document its path and filename
final File file = new File(context.getCacheDir(), "documents/document.pdf");
// let the FileProvider generate an URI for this private file
final Uri uri = FileProvider.getUriForFile(context, "com.mydomain.fileprovider", file);
// create an intent, so the user can choose which application he/she wants to use to share this file
final Intent intent = ShareCompat.IntentBuilder.from(context)
	.setType("application/pdf")
	.setSubject(context.getString(R.string.share_subject))
	.setStream(uri)
	.setChooserTitle(R.string.share_title)
	.createChooserIntent()
	.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
	.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

context.startActivity(intent);

As a bonus, I have created a sample project in Android Studio, you can download it here.