user3669004 user3669004 - 4 months ago 29
Android Question

Open file in cache directory with ACTION_VIEW

I have been searching for this for a while now but it I can't make this to work correctly. Let me explain.

I have an android application that saves files (images, documents, ...) in the cache directory. At first I used to

getExternalCacheDir()
method and save them over there but because it should be cached on devices that do not have an SD-card, I have to use
getCacheDir()
.

When I used to
getExternalCacheDir()
method, it was no problem to open those files in another application like this:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), mimetype);


But when using the
getCacheDir()
, those files are saved in the application sandbox and are not accessible from outside the application. So I googled it and came on the **ContentProvider. A ContentProvider makes it possible to open private files with external applications. But when I try to implement it, it doesn't work.

I tried implementing the ContentProvider thanks to this post: How to open private files saved to the internal storage using Intent.ACTION_VIEW? but with no success.

package com.myapplication.providers;

import java.io.File;
import java.io.FileNotFoundException;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.ParcelFileDescriptor;

public class FileProvider extends ContentProvider {

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File privateFile = new File(getContext().getCacheDir(), uri.getPath());
return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY);
}

@Override
public int delete(Uri arg0, String arg1, String[] arg2) {
return 0;
}

@Override
public String getType(Uri arg0) {
return null;
}

@Override
public Uri insert(Uri arg0, ContentValues arg1) {
return null;
}

@Override
public boolean onCreate() {
return false;
}

@Override
public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
String arg4) {
return null;
}

@Override
public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
return 0;
}
}


Added this provider to the application manifest

<provider
android:name="com.myapplication.providers.FileProvider"
android:authorities="com.myapplication"
android:exported="true" />


And using this code to open the file:

Uri uri = Uri.parse("content://com.myapplication/" + filename);

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setDataAndType(uri, mimetype);


Thanks in advance!

Answer

I found out that I had to do things differently.

Instead of creating my own ContentProvider, the v4 support library offers a FileProvider class that can be used.

In AndroidManifest.xml add

<application ...>

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="be.myapplication"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

</application>

The FILE_PROVIDER_PATHS is an xml file that describes which files can be read by other applications.

So I added a file_paths.xml file to the res/xml folder.

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <cache-path name="my_cache" path="." />
</paths>

And the only thing you have to do then is create and start the intent to show the file.

File file = new File(getCacheDir(), "test.pdf");

Uri uri = FileProvider.getUriForFile(context, "be.myapplication", file);

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, "application/pdf");

startActivity(intent);

And actually that's it.