I'm looking for a way to correctly share (not OPEN) an internal file with external application using Android Support library's FileProvider.
Following the example on the docs,
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.supportv4.my_files"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/my_paths" />
</provider>
and using ShareCompat to share a file to other apps as follows:
ShareCompat.IntentBuilder.from(activity)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
does not work, since the FLAG_GRANT_READ_URI_PERMISSION only grants permission for the Uri specified on the data
of the intent, not the value of the EXTRA_STREAM
extra (as was set by setStream
).
I tried to compromise security by setting android:exported
to true
for the provider, but FileProvider
internally checks if itself is exported, when so, it throws an exception.
To make FileProvider work follow these three steps: Define the FileProvider in your AndroidManifest file. Create an XML file that contains all paths that the FileProvider will share with other applications. Bundle a valid URI in the Intent and activate it.
If you are using Recylerview Adapter to pass image in full to DetailActivity, this solution will help you share the image received in DetailActivity so that you can share with social media apps. Drawable drawable=imageView. getDrawable(); Bitmap bitmap=((BitmapDrawable)drawable).
You can try this solution for get real path from file provider URI. First you need to define file provider path file as following. Declare file provider in AndroidMenifest. xml as following.
Content providers can help an application manage access to data stored by itself, stored by other apps, and provide a way to share data with other apps. They encapsulate the data, and provide mechanisms for defining data security.
Using FileProvider
from support library you have to manually grant and revoke permissions(at runtime) for other apps to read specific Uri. Use Context.grantUriPermission and Context.revokeUriPermission methods.
For example:
//grant permision for app with package "packegeName", eg. before starting other app via intent
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
//revoke permisions
context.revokeUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
As a last resort, if you can't provide package name you can grant the permission to all apps that can handle specific intent:
//grant permisions for all apps that can handle given intent
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
...
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
Alternative method according to the documentation:
- Put the content URI in an Intent by calling setData().
- Next, call the method Intent.setFlags() with either FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION or both.
Finally, send the Intent to another app. Most often, you do this by calling setResult().
Permissions granted in an Intent remain in effect while the stack of the receiving Activity is active. When the stack finishes, the
permissions are automatically removed. Permissions granted to one
Activity in a client app are automatically extended to other
components of that app.
Btw. if you need to, you can copy source of FileProvider and change attachInfo
method to prevent provider from checking if it is exported.
Fully working code sample how to share file from inner app folder. Tested on Android 7 and Android 5.
AndroidManifest.xml
</application>
....
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="android.getqardio.com.gmslocationtest"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
xml/provider_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="share"
path="external_files"/>
</paths>
Code itself
File imagePath = new File(getFilesDir(), "external_files");
imagePath.mkdir();
File imageFile = new File(imagePath.getPath(), "test.jpg");
// Write data in your file
Uri uri = FileProvider.getUriForFile(this, getPackageName(), imageFile);
Intent intent = ShareCompat.IntentBuilder.from(this)
.setStream(uri) // uri from FileProvider
.setType("text/html")
.getIntent()
.setAction(Intent.ACTION_VIEW) //Change if needed
.setDataAndType(uri, "image/*")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(intent);
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