1

As a preface, I am familiar with coding (2 years in highschool) but am a complete novice when it comes to Android Studio (and Java, in general). I've been searching for solutions for my problem, but due to my inexperience I have no idea how to implement the solutions to my project.

In short, I need to download a pdf from some external URL, store it into external storage, and display it using some pdf-viewer application. I've been getting error:

...

android.os.FileUriExposedException: file:///storage/emulated/0/pdf/Read.pdf exposed beyond app through Intent.getData()

...

I've been using this source on using an intent to open a pdf-viewer and this source on "File Provider" as references.

Below is what I have so far:

Fire (with a clickable TextView that should download and display the pdf)

public class Fire extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fire);
    final TextView testButton = findViewById(R.id.testPDF);
    // File file = getFilesDir();

    testButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(Fire.this, pdfView.class);
            startActivity(intent);
        }
    });

pdfView activity

public class pdfView extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pdf_view);
    String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
    File folder = new File(extStorageDirectory, "pdf");
    folder.mkdir();
    File file = new File(folder, "Read.pdf");
    try {
        file.createNewFile();
    } catch (IOException e1) {
        e1.printStackTrace();
    }
    Downloader.DownloadFile(__PDF_URL___, file);

    showPdf();
}
public void showPdf()
{
    File file = new File(Environment.getExternalStorageDirectory()+"/pdf/Read.pdf");
    PackageManager packageManager = getPackageManager();
    Intent testIntent = new Intent(Intent.ACTION_VIEW);
    testIntent.setType("application/pdf");
    List list = packageManager.queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri uri = Uri.fromFile(file);
    intent.setDataAndType(uri, "application/pdf");
    startActivity(intent);
}

Downloader class

public class Downloader {

public static void DownloadFile(String fileURL, File directory) {
try {

        FileOutputStream f = new FileOutputStream(directory);
        URL u = new URL(fileURL);
        HttpURLConnection c = (HttpURLConnection) u.openConnection();
        c.setRequestMethod("GET");
        c.setDoOutput(true);
        c.connect();

        InputStream in = c.getInputStream();

        byte[] buffer = new byte[1024];
        int len1 = 0;
        while ((len1 = in.read(buffer)) > 0) {
            f.write(buffer, 0, len1);
        }
        f.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

From my research, it seems that my problem stems from my usage of URI instead of a "File provider." Also, it seems the solution that implements a "File Provider" uses Context in some form or another, and I'm confused on what the purpose of context is and how to implement it.

If you do not have answers that is fine. Any information on how to figure this out or even understand the concept is good enough for me.

Community
  • 1
  • 1
thugzook
  • 88
  • 9

1 Answers1

0

If your targetSdkVersion >= 24, then we have to use FileProvider class to give access to the particular file or folder to make them accessible for other apps.

1) First Add a FileProvider tag in AndroidManifest.xml under tag as below:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

2) Then create a provider_paths.xml file in res/xml folder. Folder may be needed to created if it doesn't exist.

<paths>
    <external-path name="external_files" path="."/>
</paths>

3) Now create PdfDownload.java class file and paste below code:

public class PdfDownload extends Activity {
    TextView tv_loading;
    Context context;

    int downloadedSize = 0, totalsize;
    float per = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        isStoragePermissionGranted();
        super.onCreate(savedInstanceState);

        tv_loading = new TextView(this);
        tv_loading.setGravity(Gravity.CENTER);
        tv_loading.setTypeface(null, Typeface.BOLD);
        setContentView(tv_loading);
        downloadAndOpenPDF();
    }


    public static String getLastBitFromUrl(final String url) {
        return url.replaceFirst(".*/([^/?]+).*", "$1");
    }

    void downloadAndOpenPDF() {
        final String download_file_url = getIntent().getStringExtra("url");
        new Thread(new Runnable() {
            public void run() {
                Uri path = Uri.fromFile(downloadFile(download_file_url));
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    Uri uri = FileProvider.getUriForFile(PdfDownload.this, BuildConfig.APPLICATION_ID, downloadFile(download_file_url));
                    intent.setDataAndType(uri, "application/pdf");
                    startActivity(intent);
                    finish();


                } catch (ActivityNotFoundException e) {
                    tv_loading
                            .setError("PDF Reader application is not installed in your device");
                }
            }
        }).start();

    }

    File downloadFile(String dwnload_file_path) {
        File file = null;
        try {
            URL url = new URL(dwnload_file_path);
            HttpURLConnection urlConnection = (HttpURLConnection) url
                    .openConnection();

            urlConnection.connect();

            String test = getLastBitFromUrl(dwnload_file_path);
            String dest_file_path = test.replace("%20", "_");
            // set the path where we want to save the file
            File SDCardRoot = Environment.getExternalStorageDirectory();
            // // create a new file, to save the downloaded file
            file = new File(SDCardRoot, dest_file_path);
            if (file.exists()) {
                return file;
            }
            FileOutputStream fileOutput = new FileOutputStream(file);

            // Stream used for reading the data from the internet
            InputStream inputStream = urlConnection.getInputStream();

            // this is the total size of the file which we are
            // downloading
            totalsize = urlConnection.getContentLength();
            setText("Starting PDF download...");

            // create a buffer...
            byte[] buffer = new byte[1024 * 1024];
            int bufferLength = 0;

            while ((bufferLength = inputStream.read(buffer)) > 0) {
                fileOutput.write(buffer, 0, bufferLength);
                downloadedSize += bufferLength;
                per = ((float) downloadedSize / totalsize) * 100;
                if ((totalsize / 1024) <= 1024) {
                    setText("Total PDF File size  : " + (totalsize / 1024)
                            + " KB\n\nDownloading PDF " + (int) per + "% complete");
                } else {
                    setText("Total PDF File size  : " + (totalsize / 1024) / 1024
                            + " MB\n\nDownloading PDF " + (int) per + "% complete");
                }
                // setText("configuring your book pleease wait a moment");
            }
            // close the output stream when complete //
            fileOutput.close();
            // setText("Download Complete. Open PDF Application installed in the device.");
            setText("configuaration is completed now your book is ready to read");
        } catch (final MalformedURLException e) {
            setTextError("Some error occured. Press back and try again.",
                    Color.RED);
        } catch (final IOException e) {
            setTextError("Some error occured. Press back and try again.",
                    Color.RED);
        } catch (final Exception e) {
            setTextError(
                    "Failed to download image. Please check your internet connection.",
                    Color.RED);
        }
        return file;
    }

    void setTextError(final String message, final int color) {
        runOnUiThread(new Runnable() {
            public void run() {
                tv_loading.setTextColor(color);
                tv_loading.setText(message);
            }
        });
    }

    void setText(final String txt) {
        runOnUiThread(new Runnable() {
            public void run() {
                tv_loading.setText(txt);
            }
        });

    }

    private static final String TAG = "MyActivity";

    public boolean isStoragePermissionGranted() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                Log.v(TAG, "Permission is granted");
                return true;
            } else {

                Log.v(TAG, "Permission is revoked");
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                return false;
            }
        } else { //permission is automatically granted on sdk<23 upon installation
            Log.v(TAG, "Permission is granted");
            return true;
        }


    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Log.v(TAG, "Permission: " + permissions[0] + "was " + grantResults[0]);

        }
    }

}
Arbaz Alam
  • 1,172
  • 1
  • 14
  • 24