Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VIEW intent from a Content Provider URI?

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.

like image 969
Neal Sanche Avatar asked Feb 04 '11 00:02

Neal Sanche


People also ask

How do you read data from a content provider?

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.

What is intent URL?

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.

What is the use of intent createChooser () method?

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.

What does ContentResolver query () return?

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.


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

like image 156
CommonsWare Avatar answered Oct 26 '22 01:10

CommonsWare


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.

like image 30
danny117 Avatar answered Oct 26 '22 00:10

danny117