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.
To retrieve data from a provider, your application needs "read access permission" for the provider. You can't request this permission at run-time; instead, you have to specify that you need this permission in your manifest, using the <uses-permission> element and the exact permission name defined by the provider.
In this way, an Intent is like the dual of a hyperlink. With a hyperlink, the source page specifies the exact URL to be navigated to. With an Intent, the source page specifies the nature of the task to be done, and the user can select any of a number of applications to be used to complete the task.
createChooser(intent, title); // Try to invoke the intent. // Define what your app should do if no activity can handle the intent. This displays a dialog with a list of apps that respond to the intent passed to the createChooser() method and uses the supplied text as the dialog title.
The ContentResolver. query() client method always returns a Cursor. This cursor contains the column specified by the query projection. The cursor object provides read access to rows and columns.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With