5

All I wanted was to open a pdf file in my External Downloads Directory.

I used this code to open the file, and it used to work fine.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
            + File.separator + filename);
    Uri path = Uri.fromFile(file);
    Intent pdfOpenintent = new Intent(Intent.ACTION_VIEW);
    pdfOpenintent.setDataAndType(path, "application/" + getExtension(filename));
    pdfOpenintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    try {
        CourseModulesActivity.this.startActivity(pdfOpenintent);
    } catch (ActivityNotFoundException e) {
        pdfOpenintent.setType("application/*");
        startActivity(Intent.createChooser(pdfOpenintent, "No Application found to open File - " + filename));
    }

Now in android N(API 24+), it crashes saying android.os.FileUriExposedException

I followed the link - https://stackoverflow.com/a/38858040/4788557 and https://developer.android.com/training/secure-file-sharing/share-file.html#ShareFile to convert my code to this -

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
            + File.separator + filename);
    Uri path = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
    Intent pdfOpenintent = new Intent(Intent.ACTION_VIEW);
    pdfOpenintent.setData(path);
    pdfOpenintent.setType("application/" + getExtension(filename));
    pdfOpenintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
    pdfOpenintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    try {
       CourseModulesActivity.this.startActivity(pdfOpenintent);
    } catch (ActivityNotFoundException e) {
        pdfOpenintent.setType("application/*");
        startActivity(Intent.createChooser(pdfOpenintent, "No Application found to open File - " + filename));
    }

and added provider xml as -

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>

and manifest as -

<manifest...>
....
<application...>
....
    <provider
        android:name="android.support.v4.content.FileProvider"
        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>
....
</application>

So, now the app doesn't crash on N, but the pdf applications are unable to open the file.
Any help in this direction?

Community
  • 1
  • 1
Harshit
  • 623
  • 7
  • 22

1 Answers1

4

I used this code to open the file, and it used to work fine.

There are at least two bugs in that code:

  • "application/" + getExtension(filename) is not an appropriate algorithm for finding the MIME type for a file, as not all MIME types begin with application/ (e.g., image/jpeg, text/plain). Use MimeTypeMap.

  • setType() clears the data previously set on the Intent, so your Intent has no Uri.

Plus, do not use concatenation to build a file path, as your code does not handle the case where somebody decided to have DIRECTORY_DOWNLOADS end in a /. Replace:

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()
        + File.separator + filename);

with:

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fiename);

but the pdf applications are unable to open the file

There is no Uri in your Intent, as your second code snippet contains the same bug as does the first code snippet. Use setDataAndType() to set both the Uri and the MIME type at one time. Or, just use setData() and get rid of setType(), as FileProvider will use MimeTypeMap to determine a MIME type.

FWIW, this sample app demonstrates using FileProvider to serve a PDF file to PDF viewers.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • 1
    those are all specifically application type pdf/xlxs etc files... The real bug was `setDataAndType()` Thanks. – Harshit Jan 17 '17 at 21:53