I'm currently developing an application which creates an excel file(with apache poi) from the sqlite database in android. Everything is working well, except that I can't send the file to another app.
I implemented an fileprovider so I other apps can have permission to my file:
AndroidManifest.xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.ria.masterdetailnoten"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths"></meta-data>
</provider>
file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="backup" path="backup/"/>
</paths>
ExcelFile.java:
Uri uriFile = FileProvider.getUriForFile(ct, "com.example.ria.masterdetailnoten", backupFile);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setDataAndType(uriFile, "application/vnd.ms-excel");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.createChooser(intent, "Choose");
ct.startActivity(intent);
I know for sure that my file is being created and I can read it again with Apache POI. So the only things which doesn't work is sending the intent to another app.
As an example if I choose Google Drive in the intent chooser then following error appears:
"Upload failed: request contained no data."
If I open it with the Mailbox App following error appears:
"Mailbox could not read the selected file"
I hope you can help me guys, I appreciate your help!
Have you tried using: putExtra(Intent.EXTRA_STREAM, uriFile) instead of: setData(AndType)?
I've managed to work this out with intent.selector = Intent(Intent.ACTION_SEND).apply { data = Uri.parse("file:") }
Related
I am building a photo vault (that helps users hide photos) for Android 11 onwards since the birth of mighty "scoped storage" I am able to hide photos by just moving them to the app's private directory.
Now the problem arises when I want to share an image without moving it to the public (shared) directory.
I followed the implemented FileProvider, which converts the image path to URI but when I share the content URI via an intent, the following error pops up in logcat and the receiver application can not read the image.
Permission Denial: reading androidx.core.content.FileProvider uri content://com.androidbull.incognito.vaultreborn.provider/photos/Screenshot_20211221-105658.jpg from pid=10376, uid=1000 requires the provider be exported, or grantUriPermission()
Here is the code I wrote to perform sharing
val imagePath = File(this.filesDir, "photos")
val newImageFile = File(imagePath, currentImage.imageName)
val imageUri = FileProvider.getUriForFile(this, "$packageName.provider", newImageFile)
//
val shareContentIntent = Intent(Intent.ACTION_SEND)
shareContentIntent.data = imageUri
shareContentIntent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
startActivity(Intent.createChooser(shareContentIntent, null))
and just in case you are wondering here is the my provider in AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths">
</meta-data>
</provider>
And finally the file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path
name="photos"
path="photos/" />
</paths>
Can anyone please help me here?
Thank you :)
Okay, so it turned out that there was a bug in my head, not in the code.
I was only testing it for the very first image that I moved into my vault, somehow this image was showing perfectly but had a file size of 0 bytes, so that was the problem. I tested other images and worked perfectly.
Thanks, everyone for paying attention :)
There are many posts on this topic, but I can't find the solution for my problem...
Following: I would like to send a file out of my app via an e-mail attachment.
Sending the file via Whatsapp, save to Google Drive,... works, but not for K-9 Mail or Gmail ("Unable to attache file" Toast message is displayed).
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
intentShareFile.setType("application/zip");
intentShareFile.putExtra(Intent.EXTRA_STREAM, Uri.parse("/sdcard/Download/ExportFile.zip"));
//intentShareFile.putExtra(Intent.EXTRA_TEXT, "message");
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intentShareFile.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
I don't understand why it works for all apps, except e-mail apps.
Can anyone help me out?Thanks in advance.
I think your issue is related to the path of the file you are using here (i.e Uri.parse("/sdcard/Download/ExportFile.zip"))
There is a code example to send an email with an attachment on the Android Developers docs here. In the example they pass the Uri as follows:
Uri.parse("content://path/to/email/attachment"). Notice this is called a ContentUri, read more about it form here.
Hope this turns useful for you!
Amr EIZawawy is right. I need to create the Uri of the file and use the File Provider API.
I don't know if I did too much, but this is my solution:
Create a file called "file_paths.xml" in a directory "xml" (create first) within your "res" directory (sibling of layout directory).
The file needs to contain the path to the file you want to share. For the external Download directory this is:
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="download" path="Download/"/>
</paths>
// The internal path you'd be <files-path name.../> see [FileProvider][1] for all possibilities
Define the File provider within the AndroidManifest.xml with a meta-data link to the just created XML file:
<application
...
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.<your-app-package-name>.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/file_paths" /> // Link to the above created file
</provider>
...
Implement the code:
Intent intentShareFile = new Intent(Intent.ACTION_SEND);
intentShareFile.setType("application/zip");
File fileDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
File newFile = new File(fileDirectory, "ExportFile.zip");
String authority = "com.bennoss.myergometertrainer.fileprovider"; // Must be the same as in AndroidManifest.
Uri contentUri = FileProvider.getUriForFile(getContext(), authority, newFile);
intentShareFile.putExtra(Intent.EXTRA_STREAM, contentUri);
//intentShareFile.putExtra(Intent.EXTRA_TEXT, "xxx");
intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intentShareFile, "Share File"));
For more information see: FileProvider
Issue: What happens is after sharing the video when i launch the app to check the shared video it shows me error of "Unable to import" and "The video is no longer available".
Android Version: Oreo(8.0)
Code: I am trying with the following code to share videos with other apps using intent:
Intent share = new Intent(Intent.ACTION_SEND);
share.setType("video/*");
share.putExtra(Intent.EXTRA_SUBJECT, "abc");
share.putExtra(Intent.EXTRA_TITLE, "abcd");
File imageFileToShare = new File(Environment.getExternalStorageDirectory() + "/mygallery/" + "airp.mp4");
Uri uri = FileProvider.getUriForFile(Main2Activity.this, "abc.dcf.fileprovider", imageFileToShare);
share.putExtra(Intent.EXTRA_STREAM, uri);
share.setPackage("com.abc.in");
startActivity(Intent.createChooser(share, "Message"));
The above code is working fine to share Images but its not working to share videos.
What i have tried: I have tried by sharing various file types like 3gp, mkv, mp4 but nothing is working out and I have also went through various similar questions but no solution is working in case of videos.
Please help me with this issue and also if there is any alternate way to do so?
You can add this to your OnCreate
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
If that doesn't work for you,
Add Provider to Manifest by doing this
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths"/>
</provider>
Remember to use android:name="androidx.core.content.FileProvider" if you're using androidx
Create a file in xml directory (res/xml) and name provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
Replace
Uri uri = FileProvider.getUriForFile(Main2Activity.this, "abc.dcf.fileprovider", imageFileToShare);
with this
Uri uri = FileProvider.getUriForFile(Main2Activity.this, BuildConfig.APPLICATION_ID + ".provider",imageFileToShare);
My App is creating a PDF and passes it to other Apps to be displayed elsewhere. I create the file in internal storage to have to ask the user for less permissions.
I create my intent via:
Intent viewIntent = new Intent(Intent.ActionView);
Java.IO.File document = new Java.IO.File(filePath);
Android.Net.Uri contentUri = FileProvider.GetUriForFile(
_context,
_context.PackageName + ".provider",
document);
viewIntent.SetDataAndType(contentUri, GetMimeType(document));
viewIntent.SetFlags(ActivityFlags.NewTask);
viewIntent.AddFlags(ActivityFlags.GrantReadUriPermission);
viewIntent.AddFlags(ActivityFlags.ClearTask);
viewIntent.AddFlags(ActivityFlags.GrantPersistableUriPermission);
viewIntent.AddFlags(ActivityFlags.GrantPrefixUriPermission);
viewIntent.AddFlags(ActivityFlags.GrantWriteUriPermission);
Intent chooser = Intent.CreateChooser(viewIntent, "");
chooser.SetFlags(ActivityFlags.NewTask);
chooser.AddFlags(ActivityFlags.GrantReadUriPermission);
chooser.AddFlags(ActivityFlags.ClearTask);
chooser.AddFlags(ActivityFlags.GrantPersistableUriPermission);
chooser.AddFlags(ActivityFlags.GrantPrefixUriPermission);
chooser.AddFlags(ActivityFlags.GrantWriteUriPermission);
_context.StartActivity(viewIntent);
On the Google Pixel 3 XL where I test, I can open a PDF without any issues.
When I do the same on a Huawei tablet with API level 24, sometimes everything works but at other times Adobe Acrobat shows an error: This file could not be accessed. Check the location or the network and try again.
The behavior isn't deterministic, sometimes I get the error but other times everything works fine.
In the Application node of your Android Manifest make sure you've added a FileProvider definition:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.FileProvider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="#xml/provider_paths" />
</provider>
Add a Resources/xml/provider_paths file with the contents:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>
You can restrict this later if needed. However, for my use case I put files in external storage, which I share from. You need to adjust this accordingly where you are sharing from.
I.e. if you are sharing the file from internal app storage you will need to add a files-path definition there too.
Then when sharing a file you simply do:
var packageName = context.ApplicationInfo.PackageName;
var fileProviderName = $"{packageName}.FileProvider";
var intent = new Intent(Intent.ActionSend);
intent.AddFlags(ActivityFlags.GrantReadUriPermission);
intent.SetType("image/*"); // change mime type if wanting to open in other app
intent.PutExtra(Intent.ExtraStream,
FileProvider.GetUriForFile(context, fileProviderName, new Java.IO.File(filePath)));
StartActivity(intent);
That should be enough, works fine every time for me for sharing images to another app. I don't think you need the flags for your chooser Intent, only for the inner viewIntent. Also the GrantReadUriPermission should be the only thing needed if you are providing flags.
* NEW INFO *
Singlestepping into the FileProvider code, I see that external-files-path is not actually rooted at /storage/emulated/0/ but at /storage/emulated/0/Android/data/com.perinote.perinote/files/. So, I ask the question, how do I share a file in external storage /DCIM?
I'm passing an "external" file to an Intent and getting an "IllegalArgumentException: Failed to find configured root that contains ..." error.
Not sure what's missing. Doing something similar with a "cache" file works.
Here's my provider part of my manifest:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.perinote.image_access"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/filepaths" />
</provider>
The "filepaths.xml" file:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
path="DCIM/Perinote/" name="images" /> <!-- must be same as ImageData.getPerinoteDirectory -->
</paths>
And the snip of code, where "file" is the file name (without path) in question:
Intent intent = new Intent (Intent.ACTION_SEND);
intent.setType ("plain/text");
intent.putExtra (Intent.EXTRA_EMAIL, new String[] {address});
intent.putExtra (Intent.EXTRA_SUBJECT, subject);
intent.putExtra (Intent.EXTRA_TEXT, body);
Uri uri = FileProvider.getUriForFile (getContext (), "com.perinote.image_access", file);
intent.putExtra (Intent.EXTRA_STREAM, uri);
intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult (intent, ACTIVITY_REQUEST_SEND_EMAIL);
It's failing in getUriForFile().
When I use a file manager app on the device, the file is definitely there, under DCIM/Perinote/
For example, using the debugger, file is "IMG_20171130_204015.jpg".
And opening an explorer window on Windows shows the path
This PC\Moto G (4)\Internal shared storage\DCIM\Perinote
and the file appears in the list below.
You should start with changing <external-files-path to <external-path..