Share content: Permission Denial when using Intent.createChooser - android

I am currently adapting my app to Android 11.
When I want to share a file, I get the following error message in logcat:
Permission Denial: reading androidx.core.content.FileProvider uri
content://... from pid=20333, uid=1000 requires the provider be
exported, or grantUriPermission()
But my app is still working and sharing works without problems.
The problem only occurs when I use:
startActivity(Intent.createChooser(intent, "share"));
It does not occur when I use:
startActivity(intent);
In the AndroidManifest.xml of course I set android:requestLegacyExternalStorage to true. targetSdkVersion is 29.
Uri uri = FileProvider.getUriForFile(this, AUTHORITY_FILE, file);
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_SUBJECT, "subject");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "share")); // error
startActivity(intent); // no error

Use ShareCompat.IntentBuilder. It is doing all the right things for you.
For the underlying issue, see https://issuetracker.google.com/issues/173137936
Uri uri = FileProvider.getUriForFile(context, AUTHORITY_FILE, file);
//TODO: Use the proper MIME type. text/plain is for sharing text via EXTRA_TEXT!
// If the file actually contains text, use the type "text/*".
new ShareCompat.IntentBuilder(context)
.setType("text/plain")
.addStream(uri)
.setSubject("subject")
.setChooserTitle("share")
.startChooser();

Related

Android Sharing files without FileProvider

I have tried to share a file using the Android Sharesheet, and the guide says I should use the FileProvider.
But in my situation, I can't use the FileProvider due to APK size.
Is there any way to share a file via the Android Sharesheet without using the FileProvider?
A file that I want to share is in the external storage such as "/sdcard/abc/file.zip".
I have already tried this, but it didn't work.
Uri uri = Uri.parse("file://" + file_path); // FileUriExposedException
// Uri uri = Uri.parse("content://" + file_path); // when sharing via GMail app, a toast appears with message "Couldn't attach file"
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/zip");
intent.putExtra(Intent.EXTRA_STREAM, uri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(intent, "Share"));

Cannot grant File Provider Permissions to com.android.mms

I am trying to send a bitmap from the cache directory of my app to another external application by granting temporary read permissions through a file provider. When I select the messaging application on my phone (package name : "com.android.mms") the messaging applications crashes and I get the error:
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.example.brandon.emojimms2/shared_images/image.png from pid=9804, uid=10024 requires the provider be exported, or grantUriPermission()
Here is a screenshot of the entire error printout if needed:
I only get this error when selecting com.android.mms from the intent chooser. Every other application that I choose sends the bitmap without error. Even the messaging system on newer phones (com.google.android.apps.messaging) are able to send the bitmap with the fileprovider without any errors. I checked this with several emulated phones and apps, and the results always come out the same. The only app that I found that has a problem with file provider is "com.android.mms".
Here is the code where I share the intent:
private void shareImage()
{
File imagePath = new File(mContext.getCacheDir(), "images");
File newFile = new File(imagePath, "image.png");
Uri contentUri = FileProvider.getUriForFile(mContext, "com.example.brandon.emojimms2", newFile);
if (contentUri != null) {
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
shareIntent.setDataAndType(contentUri, mContext.getContentResolver().getType(contentUri));
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
startActivity(Intent.createChooser(shareIntent, "Choose an app"));
}
}
I even tried a solution recommended in another stackoverflow post that suggested to grant each individual intent activity write and read uri permissions, but that also did not work.

How to start the "set as" intent (wallpaper, contact picture, etc)

I searched over the web during the last few weeks (seriously) but I can't find what I need. I just would like to start an intent corresponding to the set as action. It generally offers either Set as wallpaper or Set as contact picture. And then, if more application are installed on the device, they can be listed as well.
Here is an example of what I want :
I precise that I need to support API level 14 and higher.
I found getCropAndSetWallpaperIntent but it works only with content URI which is a problem for me, and is only availbable on API lvl 19 and higher.
I found the answer by my self :
Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.setDataAndType(imageUri, "image/*");
intent.putExtra("jpg", "image/*");
startActivityForResult(Intent.createChooser(intent,
getString(R.string.set_as)), REQUEST_ID_SET_AS_WALLPAPER);
You just have to ensure that the uri is public and will be reachable by the crop application chosen by the user.
This solution worked for me with Uri:
Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.setDataAndType(contentUri, "image/*");
intent.putExtra("mimeType", "image/*");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(Intent.createChooser(
intent, "Set as:"));
This worked for me :
Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
//can't use normal URI, because it requires the Uri from file
intent.setDataAndType(Uri.fromFile(new File(uriOfImage)),"image/*");
intent.putExtra("mimeType","image/*");
startActivity(Intent.createChooser(intent,"Set Image"));
You can check that the URI that you pass, should contain the 'file://' prefix (It doesn't work without that).

Android - Set Wallpaper using the "Set Wallpaper" intent

The question was asked here and here but there was no real answer.
Android has a built-in "Set Wallpaper" feature, such feature is available when starting an activity intent with mime "image/jpeg" or long-tapping on images in browser.
My question is: how do I programmatically invoke the built-in "Set Wallpaper" feature using a file Uri?
Seems like there is no answer to the question however I did discover a workaround:
Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(uri, "image/jpeg");
intent.putExtra("mimeType", "image/jpeg");
this.startActivity(Intent.createChooser(intent, "Set as:"));
For me work only:
Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(uri, "image/*");
intent.putExtra("mimeType", "image/*");
this.startActivity(Intent.createChooser(intent, "Set as:"));
If mime "image/jpeg" apps not found image.
If your app crashes after choosing the application you want to set wallpaper with, you need to add
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
A typical example is attachments in a email app. Access to the emails should be protected by permissions, since this is sensitive user data. However, if a URI to an image attachment is given to an image viewer, that image viewer no longer has permission to open the attachment since it has no reason to hold a permission to access all email.
The solution to this problem is per-URI permissions: when starting an activity or returning a result to an activity, the caller can set Intent.FLAG_GRANT_READ_URI_PERMISSION and/or Intent.FLAG_GRANT_WRITE_URI_PERMISSION. This grants the receiving activity permission access the specific data URI in the intent, regardless of whether it has any permission to access data in the content provider corresponding to the intent.
https://developer.android.com/guide/topics/permissions/overview
val intent = Intent(Intent.ACTION_ATTACH_DATA)
.apply {
addCategory(Intent.CATEGORY_DEFAULT)
setDataAndType(uri, "image/*")
putExtra("mimeType", "image/*")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
startActivity(Intent.createChooser(intent, "Set as:"))
And for me work only
Intent intent = new Intent();
intent.setAction(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(ContactsContract.Contacts.CONTENT_URI, "image/*");
intent.putExtra("mimeType", "image/*");
startActivityForResult(Intent.createChooser(intent, "Select service:"), position);
position - it is your : getIntent().getExtras().getInt("id_test");

Trying to attach a file from SD Card to email

I am trying to launch an Intent to send an email. All of that works, but when I try to actually send the email a couple 'weird' things happen.
here is code
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("image/jpeg");
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Photo");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://sdcard/dcim/Camera/filename.jpg"));
sendIntent.putExtra(Intent.EXTRA_TEXT, "Enjoy the photo");
startActivity(Intent.createChooser(sendIntent, "Email:"));
So if I launch using the Gmail menu context It shows the attachment, lets me type who the email is to, and edit the body & subject. No big deal. I hit send, and it sends. The only thing is the attachment does NOT get sent.
So. I figured, why not try it w/ the Email menu context (for my backup email account on my phone). It shows the attachment, but no text at all in the body or subject. When I send it, the attachment sends correctly. That would lead me to believe something is quite wrong. Do I need a new permission in the Manifest launch an intent to send email w/ attachment? What am I doing wrong?
Also getting the same problem
Code:
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("image/jpeg");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[]
{"me#gmail.com"});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"Test Subject");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
"go on read the emails");
Log.v(getClass().getSimpleName(), "sPhotoUri=" + Uri.parse("file:/"+ sPhotoFileName));
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:/"+ sPhotoFileName));
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
From adb logcat:
V/DumbDumpersMain( 3972): sPhotoUri=file://sdcard/DumbDumpers/DumbDumper.jpg
I/ActivityManager( 56): Starting activity: Intent { action=android.intent.action.CHOOSER comp={android/com.android.internal.app.ChooserActivity} (has extras) }
I/ActivityManager( 56): Starting activity: Intent { action=android.intent.action.SEND type=jpeg/image flags=0x3000000 comp={com.google.android.gm/com.google.android.gm.ComposeActivityGmail} (has extras) }
I/ActivityManager( 56): Starting activity: Intent { action=android.intent.action.SEND type=jpeg/image flags=0x2800000 comp={com.google.android.gm/com.google.android.gm.ComposeActivity} (has extras) }
D/gmail-ls( 120): MailProvider.query: content://gmail-ls/labels/me#gmail.com(null, null)
D/Gmail ( 2507): URI FOUND:file://sdcard/DumbDumpers/DumbDumper.jpg
Looks like the email provider is attaching a 0 length file. When I check the filesystem the file is there and correct. The code which creates the image file is well finished prior to the attempt to email it.
Anyone fixed this without magic reboots (I've already tried that)?
Update
Path for me should have been
file:///sdcard/DumbDumpers/DumbDumper.jpg
you need the extra / as this points to the root directory, i.e.:
file:// + /sdcard/DumbDumpers/DumbDumper.jpg
combined as
file:///sdcard/DumbDumpers/DumbDumper.jpg
In the above snippet you need:
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+ sPhotoFileName));
Just a little remark from my side. I've been having the same issues with GMail, but somehow it seems to work when I store the file in question on the SD card first and retrieve it from there, rather than from the assets. So my code is the following:
Intent i = new Intent(Intent.ACTION_SEND);
i.putExtra(Intent.EXTRA_SUBJECT, "Title");
i.putExtra(Intent.EXTRA_TEXT, "Content");
i.putExtra(Intent.EXTRA_STREAM, uri);
i.setType("text/plain");
startActivity(Intent.createChooser(i, "Send mail"));
and here,
uri = Uri.fromFile(new File(context.getFilesDir(), FILENAME));
does not work, whereas
uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), FILENAME));
does.
Regards,
Michael
instead of "Uri.parse" use "Uri.fromFile(new File(Environment.getExternalStorageDirectory(),"file name"))"
Environment.getExternalStorageDirectory() - path to SDcard or any other external storage
It appears that this is actually correct, not sure what was happening, but after a reboot it started working :/
Intent i = new Intent(Intent.ACTION_SEND);
i.setType("message/rfc822");
i.putExtra(Intent.EXTRA_EMAIL , new String[]{"example#mail.com"});
i.putExtra(Intent.EXTRA_SUBJECT, "Data from app");
i.putExtra(Intent.EXTRA_TEXT , "experience number x");
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "filename.txt"));
i.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(i, "Send email..."));
I got the same problem and looked everywhere for a solution. Finally I solved it by finding an open source app that worked out of the box and looked at how they did it. The code is rather long so I won't quote it here but post a link. Look at the sendEmail function in line 449
http://rehearsalassist.svn.sourceforge.net/viewvc/rehearsalassist/android/trunk/src/urbanstew/RehearsalAssistant/SessionPlayback.java?revision=94&view=markup
I refactored my code to be similar, and now it works. I hope this will help others in the same situation.
From RFC 1738 section 3.10:
A file URL takes the form:
file://<host>/<path>
where host is the fully qualified domain name of the system on
which the path is accessible, and path is a hierarchical
directory path of the form directory/directory/.../name.
So it's file:///path/from/root just like http://host/path/from/root because there's an implicit 'localhost' between the second and third slash. But as mentioned above, use Uri.FromFile() to build it.
I had the same symptoms. In my case it was because I was initially saving the attachment with the permissions MODE_PRIVATE. As soon as I changed it to MODE_WORLD_READABLE it seems GMail was then able to access the file and send the attachment properly.
See more
It's work perfectly for me:
On this solution the Nicolas create one copy inside Cache folder and here gmail intent has access!
http://stephendnicholas.com/archives/974
public void sendMail(String path) {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
new String[] {AppConstant.server_mail});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
"IBPS ERROR Mail");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT,
"This is an autogenerated mail from IBPS app");
emailIntent.setType("image/png");
Uri myUri = Uri.parse("file://" + path);
emailIntent.putExtra(Intent.EXTRA_STREAM, myUri);
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}
Also try adding Intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); This helped with my issue.
I have got solution on this after 4 days, Please note following points while giving path to File class in Android(Java):
1) Use path for internal storage String path="/storage/sdcard0/myfile.txt";
2) path="/storage/sdcard1/myfile.txt";
3) mention permissions in Manifest file.
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
4) First check file length for confirmation.
5) Check paths in ES File Explorer regarding sdcard0 & sdcard1 is this same or else......
e.g.
File file=new File(path);
long=file.length();//in Bytes
Send an email with an attachment: (By docs)
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon#example.com"});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Email subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "Email message text");
emailIntent.putExtra(Intent.EXTRA_STREAM,
Uri.parse("content://path/to/email/attachment"));
// You can also attach multiple items by passing an ArrayList of Uris

Categories

Resources