Home » Android » android – VIEW intent from a Content Provider URI?

android – VIEW intent from a Content Provider URI?

Posted by: admin May 14, 2020 Leave a comment

Questions:

I have some requirements to protect some sensitive data. The data is downloaded as a PDF from a URL and saved as an Application Private file using the following code:

public File downloadPDF(final Context fileContext, Uri reportUri, final String fileName)
{
    try
    {
        HttpGet get = new HttpGet(reportUri.toString());

        File file = httpClient.execute(get, new ResponseHandler<File>()
        {
            @Override
            public File handleResponse(HttpResponse response) throws ClientProtocolException, IOException
            {
                if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    response.getEntity().writeTo(fileContext.openFileOutput(fileName, Context.MODE_WORLD_READABLE));
                    return fileContext.getFileStreamPath(fileName);
                }
                return null;
            }
        });

        return file;
    }
    catch (IOException e)
    {
        Log.e(TAG, "Unable to download report.", e);
    }

    return null;
}

Now, what I’d like to do is change this to using Context.MODE_PRIVATE and create a ContentProvider so that my application has complete control over the sharing of this file to a PDF reader such as Adobe Reader. Is this possible? I currently use code like the following to pass the report URI to the currently configured PDF reader.

    // Fire up a PDF viewer intent for the URL.
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setDataAndType(uri, "application/pdf");
    startActivity(intent);

Would the same work through a ContentProvider type URI? A content://package/fileid type URI? I’ll be trying a little spike tomorrow to see if I can, but if anyone knows that only file:// URIs are allowed, it would be really helpful.


UPDATE

I was able to solve my problem satisfactorily by implementing a ContentProvider subclass with the following method overridden:

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException 
{
    // The filename is the path in the URI without the initial slash.
    String fileName = uri.getPath().substring(1);
    File file = getContext().getFileStreamPath(fileName);
    return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
}

Then, when I fire off the viewing intent, it is rewritten something like the following:

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.withAppendedPath(Uri.parse("content://providername/"),filePath);
intent.setData(uri);
startActivity(intent);

And in my case, I use Adobe Reader, which properly implements the loading from content:// URIs.

How to&Answers:

Would the same work through a ContentProvider type URI? A content://package/fileid type URI?

It should. You will need to have your ContentProvider return application/pdf for getType(). And, it is possible that some PDF readers won’t handle content:// Uri values.

What you can do is make the ContentProvider, then use PackageManager to see if anything will understand your ACTION_VIEW Intent on the content:// Uri. If something responds, you’re set. If not, you can fall back to making the file be world-readable and using your current implementation. Or, since changing the file to world-readable may be a pain, you could run the test early on (e.g., when your app starts) with some scrap Uri that your ContentProvider supports, so you will know which route to take when you do your downloads.

Answer:

No content providers do not protect files

In fact probably the first thing an app might do with a file from a content provider is make a temporary copy in its cache dir. I think you should revisit this.