0

I am trying to display "NEFT.pdf" file stored in the asset folder of my android app.
The following code works absolutely fine till API 25

private void CopyReadAssets(String filename) {
    AssetManager assetManager = getAssets();
    InputStream in = null;
    OutputStream out = null;
    File file = new File(getFilesDir(), filename);

    try {
        in = assetManager.open(filename);
        out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);

        copyFile(in, out);
        in.close();
        in = null;
        out.flush();
        out.close();
        out = null;


        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(
                Uri.parse("file://" + getFilesDir() + "/"+filename), "application/pdf");

        startActivity(intent);
    } catch (Exception e)
    {
        Toast.makeText(PdfFilesList.this, "cra: "+e.toString(), Toast.LENGTH_SHORT).show();
    }
}

This code does not work on API 25 and above. It gives the error MODE_WORLD_READABLE no longer supported.
I changed it to MODE_PRIVATE but that gives me another error

android.os.fileuriexposedexception exposed beyond app through intent.getdata().
So I applied the concept explained at Developer.Android.com.
This is what I get in the error log:

E/DisplayData: openFd: java.io.FileNotFoundException: No such file or directory

E/PdfLoader: Can't load file (doesn't open) Display Data [PDF : NEFT.pdf] +ContentOpenable, uri: content://com.user.plansmart.provider/pdf_files/NEFT.pdf

uri looks correct to me. Can Anyone help me in locating the error here.

Here is the provider element in the manifest file.

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

This is the provider_paths.xml file

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

This is the Android code to display the pdf file

private void displayFile(String filename) {
    try {
        File filePath = new File(getApplicationContext().getFilesDir(), "pdf");
        File newFile =  new File(filePath, filename);

        //new version
        Uri fileUri = FileProvider.getUriForFile(PdfFilesList.this,
                BuildConfig.APPLICATION_ID + ".provider", newFile);

        getApplicationContext().grantUriPermission(PACKAGE_NAME,
                            fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setDataAndType(fileUri, "application/pdf");
        intent.setFlags(FLAG_GRANT_READ_URI_PERMISSION | FLAG_GRANT_WRITE_URI_PERMISSION);
        startActivity(intent);
    }catch (Exception e){
        e.printStackTrace();
        Toast.makeText(PdfFilesList.this, "df "+e.toString(), Toast.LENGTH_SHORT).show();
    }
}
pamo
  • 111
  • 2
  • 13
  • If you have files in assets then you cannot use the FileProvider class to serve them. Rethink. Recode. – greenapps Dec 05 '17 at 10:50
  • `getFilesDir(), "pdf");`. What did you say? Assets? No. Not in assets. getFilesDir() is private internal storage. Please adapt your post. Starting with the subject. – greenapps Dec 05 '17 at 10:53
  • Or is the file in assets? Then you cannot use getFilesDir(). Please tell what you have. – greenapps Dec 05 '17 at 11:00
  • Yes, my pdf file is in asset folder. If what I'm doing is wrong please suggest me the right way. – pamo Dec 05 '17 at 11:10
  • You have at least two options. Copy the file from assets to getFilesDir(). Then you can use your posted code. There will yet be an error but more about that later if you choose this road. Further you can write your own file providet which can serve files from assets. – greenapps Dec 05 '17 at 11:15
  • I've added the older code that works absolutely fine on older APIs. I'm trying to do the same task but with a code that works on all devices. I'm new to android and am learning from the tutorials available online. This concept is kind of urgent and has already taken nearly a weeks time of mine. In case you know the answer or any helpful link do share. – pamo Dec 05 '17 at 11:48
  • No. Your code does work never for files in assets. Also not on older devices. How to do it i already told you. If you have specific questions then ask. – greenapps Dec 05 '17 at 12:00
  • `out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);` Change to `out = openFileOutput(filename);`. – greenapps Dec 05 '17 at 12:06
  • `android.os.fileuriexposedexception`. You should not have come that far if you had used a FileProvider. YOU MUST USE A FILEPROVIDER. – greenapps Dec 05 '17 at 12:09
  • I would not start yet an intent in a function i named `CopyReadAssets()`. – greenapps Dec 05 '17 at 12:11
  • `private void CopyReadAssets` Change to `private boolean CopyReadAssets`. And call the intent after this function only if it returned true. And using a file provider. Please show how you call this function. Start with the call in your code. Just call displayFile() after it. – greenapps Dec 05 '17 at 12:14
  • You should start with `String fileName = "NEFT.pdf"; if(!copyReadAssets(fileName)) return; displayFile(fileName);`. I leave it to you to add Toasts to inform the user if things go wrong. – greenapps Dec 05 '17 at 12:26
  • `I've added the older code that works absolutely fine on older APIs.`. My god.. You ment 'ive added new code to the top of my post.`. See function ... – greenapps Dec 05 '17 at 12:33
  • see the updated code below. – ChandraShekhar Kaushik Dec 05 '17 at 12:56

1 Answers1

2

Cause you wanna display the PDF file in a separate application (such as Adobe Reader), I would prefer doing the following:-

private void CopyReadAssets()
        {
            AssetManager assetManager = getActivity().getAssets();

            InputStream in = null;
            OutputStream out = null;
            String state = Environment.getExternalStorageState();
            if (!Environment.MEDIA_MOUNTED.equals(state)) {
                Toast.makeText(getActivity(), "External Storage is not Available", Toast.LENGTH_SHORT).show();
            }
            File pdfDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/PDFs");
            if (!pdfDir.exists()) {
                pdfDir.mkdir();
            }
            File file = new File(pdfDir + "/abc.pdf");

            try
            {
                in = assetManager.open("abc.pdf");
                out = new BufferedOutputStream(new FileOutputStream(file));
                copyFile(in, out);
                in.close();
                in = null;
                out.flush();
                out.close();
                out = null;
            } catch (Exception e)
            {
                Log.e("tag", e.getMessage());
            }
            if (file.exists()) //Checking for the file is exist or not
            {
                Uri path = Uri.fromFile(file);
                Intent objIntent = new Intent(Intent.ACTION_VIEW);
                objIntent.setDataAndType(path, "application/pdf");
                objIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                Intent intent1 = Intent.createChooser(objIntent, "Open PDF with..");
                try {
                    startActivity(intent1);
                } catch (ActivityNotFoundException e) {
                    Toast.makeText(getActivity(), "Activity Not Found Exception ", Toast.LENGTH_SHORT).show();
                }
            } else {
                Toast.makeText(getActivity(), "The file not exists! ", Toast.LENGTH_SHORT).show();
            }
        }

For copy file into device memory:-

private void copyFile(InputStream in, OutputStream out) throws IOException
    {
        byte[] buffer = new byte[1024];
        int read;
        while ((read = in.read(buffer)) != -1)
        {
            out.write(buffer, 0, read);
        }
    }

Use the below Permission

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • "File not exists". I tried to change the Uri Path to this Uri path = Uri.parse("content://com.user.plansmart.provider/pdf_files/"+filename); – pamo Dec 05 '17 at 10:55
  • also tried with Uri path = Uri.parse("file:///android_asset/"+filename); does'nt work – pamo Dec 05 '17 at 11:07
  • Dont spend your time on this answer. Its nonsense. – greenapps Dec 05 '17 at 12:02
  • It worked. **out = openFileOutput(file.getName(), Context.MODE_WORLD_READABLE);** method was the main culprit, which when changed to **out = new BufferedOutputStream(new FileOutputStream(file));** it gave the desired result. Thanks so much. – pamo Dec 06 '17 at 06:55