Open downloaded file on Android N using FileProvider

althaus picture althaus · Oct 12, 2016 · Viewed 25.8k times · Source

I've got to fix our App for Android N due to the FileProvider changes. I've basically read all about this topic for the last ours, but no solution found did work out for me.

Here's our prior code which starts downloads from our app, stores them in the Download folder and calls an ACTION_VIEW intent as soons as the DownloadManager tells he's finished downloading:

BroadcastReceiver onComplete = new BroadcastReceiver() {
    public void onReceive(Context ctxt, Intent intent) {
        Log.d(TAG, "Download commplete");

        // Check for our download
        long referenceId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
        if (mDownloadReference == referenceId) {
            DownloadManager.Query query = new DownloadManager.Query();
            query.setFilterById(mDownloadReference);
            Cursor c = mDownloadManager.query(query);
            if (c.moveToFirst()) {
                int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
                if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
                    String localUri = c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI));
                    String fileExtension = MimeTypeMap.getFileExtensionFromUrl(localUri);
                    String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileExtension);

                    if (mimeType != null) {
                        Intent openFileIntent = new Intent(Intent.ACTION_VIEW);
                        openFileIntent.setDataAndTypeAndNormalize(Uri.parse(localUri), mimeType);
                        openFileIntent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);

                        try {
                            mAcme.startActivity(openFileIntent);
                        }
                        catch (ActivityNotFoundException e) {
                            // Ignore if no activity was found.
                        }
                    }
                }
            }
        }
    }
};

This works on Android M, but breaks on N due to the popular FileUriExposedException. I've now tried to fix this by using the FileProvider, but I cannot get it to work. It's breaking when I try to get the content URI:

Failed to find configured root that contains /file:/storage/emulated/0/Download/test.pdf

The localUri returned from the DownloadManager for the file is:

file:///storage/emulated/0/Download/test.pdf

The Environment.getExternalStorageDirectory() returns /storage/emulated/0 and this is the code for the conversion:

File file = new File(localUri);
Log.d(TAG, localUri + " - " + Environment.getExternalStorageDirectory());
Uri contentUri = FileProvider.getUriForFile(ctxt, "my.file.provider", file);

From AndroidManifest.xml

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="my.file.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths"/>
    </provider>

The file_paths.xml:

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

And I've tried all values I could find in that xml file. :(

Answer

althaus picture althaus · Oct 14, 2016

Thanks to @greenaps I was able to solve the issue. The local URI retrieved from the DownlodManager was prefixed with file://. This has to be dropped for the new FileProvider:

if (localUri.substring(0, 7).matches("file://")) {
    localUri =  localUri.substring(7);
}
File file = new File(localUri);