6

I am trying to get file from external storage, then I have to send that file to pdf readers using intents. Previously below code was working fine but after installing Android 6 (Marshmallow update), my code is not working and getting a toast message

"This file could not be accessed Check the location or the network and try again."

(This is due to new android runtime permissions). I just tried all the solutions (Content Providers etc but not working) Any Solutions?

 File file = new File(getFilesDir(), "myfile.pdf");
 Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(Uri.fromFile(file), "application/pdf");
        intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
        Intent intentChooser = Intent.createChooser(intent, "Choose Pdf Application");
        try {
            startActivity(intentChooser);
        } catch (ActivityNotFoundException e) {
            //com.adobe.reader
            startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=com.adobe.reader")));
        }
Naveed Ahmad
  • 6,627
  • 2
  • 58
  • 83
  • so you've tried requesting this permission both in manifest and in runtime? `android.permission.READ_EXTERNAL_STORAGE` – wanpanman Jul 02 '16 at 10:41
  • Yes, I have used READ_EXTERNAL_STORAGE Permission but not working, same error – Naveed Ahmad Jul 02 '16 at 10:42
  • someone reported similar problem here http://stackoverflow.com/questions/30549807/cannot-open-pdf-file-in-external-app – wanpanman Jul 02 '16 at 10:45
  • 1
    I tried this solution but didn't work for me. I just found a solution, posted as answer. Thanks for your help and support. – Naveed Ahmad Jul 02 '16 at 10:47
  • Hope this link help you:https://stackoverflow.com/questions/19422075/open-a-selected-file-image-pdf-programmatically-from-my-android-applicat/56946633#56946633 – Rahul Kushwaha Jul 09 '19 at 06:43

4 Answers4

12

After reviewing the answers above the simplest solution is as follows.

    Uri pdf = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".provider", file);

    Intent pdfOpenintent = new Intent(Intent.ACTION_VIEW);
    pdfOpenintent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_CLEAR_TOP);
    pdfOpenintent.setDataAndType(pdf, "application/pdf");

    try {
        startActivity(pdfOpenintent);
    } catch (ActivityNotFoundException e) {
        // handle no application here....
    }

The key is to set the both the FLAG_GRANT_READ_URI_PERMISSION and FLAG_ACTIVITY_CLEAR_TOP at the same time.

Make sure to have your provider declared in the ApplicationManifest.xml

<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>

Hers is the provider_paths file

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>
mfleshman
  • 313
  • 4
  • 11
5

Just found a working solution for both previous and new Android OS:

Step 1: Create a class names SampleContentProvider

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;

import java.io.File;
import java.io.FileNotFoundException;

/**
 * Created by naveed on 7/2/16.
 */
public class SampleContentProvider extends ContentProvider {

    @Override
    public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
        File privateFile = new File(uri.getPath());
        return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
    }

    @Override
    public boolean onCreate() {
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        return null;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }
}

Step2: Add the provider in application body of manifest:

<provider
        android:name=".SampleContentProvider"
        android:authorities="your_package_name"
        android:exported="true" />

Step 3: Now finally pass the file absolute path to the intent by using Uri

  Intent intent = new Intent(Intent.ACTION_VIEW);
        File file = new File(getFilesDir(), "myfile.pdf");
        String absoluteFilePath = file.getAbsolutePath();
        String mimeType = "application/pdf";
        Uri uri = Uri.parse("content://"+"Your_package_name"+"/" + absoluteFilePath);
        intent.setDataAndType(uri, mimeType);
        Intent intentChooser = Intent.createChooser(intent, "Choose Pdf Application");
        startActivity(intentChooser);

I hope this will help you. Thanks everyone for your help.

Naveed Ahmad
  • 6,627
  • 2
  • 58
  • 83
  • 1
    A simpler and more robust solution would be to [use `FileProvider`](https://developer.android.com/reference/android/support/v4/content/FileProvider.html). Your provider, since it does not support the `OpenableColumns` and does not support `getType()`, will fail with some consumers of your content. – CommonsWare Jul 02 '16 at 11:13
  • @CommonsWare I checked your solutions but I was failed, do you have simple sample app with full documentation? – Naveed Ahmad Jul 02 '16 at 11:30
  • "With full documentation" comes in the form of [my book](https://commonsware.com/Android). Several sample apps from that book are available that show `FileProvider` in use, such as [this one](https://github.com/commonsguy/cw-omnibus/tree/master/ContentProvider/V4FileProvider), [this one](https://github.com/commonsguy/cw-omnibus/tree/master/Camera/FileProvider), and [this one](https://github.com/commonsguy/cw-omnibus/tree/master/Notifications/DownloadNotify). – CommonsWare Jul 02 '16 at 11:34
4

Use FileProvider (which is anyway the accepted approach since Android Nougat - otherwise you'll be getting android.os.FileUriExposedException) and then don't forget to grant permissions to all the packages it needs:

Uri uri = FileProvider.getUriForFile(
                                context,
                                context.getApplicationContext().getPackageName(), myFile);

                        Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
                        pdfIntent.setDataAndType(apkURI, "application/pdf");
                        pdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        pdfIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                        grantAllUriPermissions(context, pdfIntent, apkURI);

Where grantAllUriPermissions():

private void grantAllUriPermissions(Context context, Intent intent, Uri uri) {
        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);
        }
}
limlim
  • 3,115
  • 2
  • 34
  • 46
-1

Fires intents to handle files of known mime types, Code:

private void openFile(Uri fileUri) {

    String mimeType = mModel.getMimeType(fileUri);

    if (mimeType != null) { //we have determined a mime type and can probably handle the file.
        try {
            /*Implicit intent representing the action we want.  The system will determine is it
            can handle the request.*/
            Intent i = new Intent(Intent.ACTION_VIEW);
            i.setDataAndType(fileUri, mimeType);

            //We ask the Activity to start this intent.
            mView.getActivity().startActivity(i);
        } catch (ActivityNotFoundException e) {
            /*If we have figured out the mime type of the file, but have no application installed
            to handle it, send the user a message.
             */
            Toast.makeText(mView.getActivity(), "The System understands this file type," +
                            "but no applications are installed to handle it.",
                    Toast.LENGTH_LONG).show();
        }
    } else {
        /*if we can't figure out the mime type of the file, let the user know.*/
        Toast.makeText(mView.getActivity(), "System doesn't know how to handle that file type!",
                Toast.LENGTH_LONG).show();
    }
}
Ori Marko
  • 56,308
  • 23
  • 131
  • 233