I am hoping to be able to email the SQLite database I use within my app as a form of backup that the user can perform. My current code is below, the database shows up as an attachment in the email intent and the email will send, but the attachment is not sent.
File file = new File(Environment.getDataDirectory(), "/data/com.app/databases/databaseName");
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{""});
intent.putExtra(Intent.EXTRA_SUBJECT, "Backup");
intent.putExtra(Intent.EXTRA_TEXT, "");
intent.setType("application/octet-stream");
intent.putExtra(Intent.EXTRA_STREAM, file.toURI());
startActivity(Intent.createChooser(intent, "Send Email"));
Export your database to the sd card before trying to email it. You can't add attachments from within the data/data folder of your application. Those files are private to your application.
I remember I had this exact problem some time back. I'm not sure what I did to fix it, but my working code looks something like this...
File root = Environment.getExternalStorageDirectory();
String fileName = "foo.txt";
if (root.canWrite()) {
attachment = new File(root, fileName);
}
email.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment));
startActivity(Intent.createChooser(email, "Send Email"));
I do remember that since I was using the SD card for storage, it would not send my attachment if I was still plugged into my computer via USB (since it kept the SD card mounted and busy). Once I unplugged the USB connection, things worked well.
https://stackoverflow.com/a/18548685/293280
The above SO Answer does a great job at showing you how to copy the DB file, email it as an attachment, and then delete the copied file. Check it out.
watch this link : http://www.openintents.org/en/node/121 you need passed URI
the solution is:
files have to be attached as
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:///sdcard/mysong.mp3"));
You need the extra / in file:/// (missing in the Stack Overflow post) as otherwise GMail drops the attachment just before sending stating the file path is wrong.
SO YOU NEED TH EXTRA / in file and its not droped.... hope this helps
Related
My app creates mails with attachments, and uses an intent with Intent.ACTION_SEND to launch a mail app.
It works with all the mail apps I tested with, except for the new Gmail 5.0 (it works with Gmail 4.9), where the mail opens without attachment, showing the error: "Permission denied for the attachment".
There are no useful messages from Gmail on logcat. I only tested Gmail 5.0 on Android KitKat, but on multiple devices.
I create the file for the attachment like this:
String fileName = "file-name_something_like_this";
FileOutputStream output = context.openFileOutput(
fileName, Context.MODE_WORLD_READABLE);
// Write data to output...
output.close();
File fileToSend = new File(context.getFilesDir(), fileName);
I'm aware of the security concerns with MODE_WORLD_READABLE.
I send the intent like this:
public static void compose(
Context context,
String address,
String subject,
String body,
File attachment) {
Intent emailIntent = new Intent(Intent.ACTION_SEND);
emailIntent.setType("message/rfc822");
emailIntent.putExtra(
Intent.EXTRA_EMAIL, new String[] { address });
emailIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, body);
emailIntent.putExtra(
Intent.EXTRA_STREAM,
Uri.fromFile(attachment));
Intent chooser = Intent.createChooser(
emailIntent,
context.getString(R.string.send_mail_chooser));
context.startActivity(chooser);
}
Is there anything I do wrong when creating the file or sending the intent? Is there a better way to start a mail app with attachment? Alternatively - has someone encountered this problem and found a workaround for it?
Thanks!
I was able to pass a screenshot .jpeg file from my app to GMail 5.0 through an Intent. The key was in this answer.
Everything I have from #natasky 's code is nearly identical but instead, I have the file's directory as
context.getExternalCacheDir();
Which "represents the external storage directory where you should save cache files" (documentation)
GMail 5.0 added some security checks to attachments it receives from an Intent. These are unrelated to unix permissions, so the fact that the file is readable doesn't matter.
When the attachment Uri is a file://, it'll only accept files from external storage, the private directory of gmail itself, or world-readable files from the private data directory of the calling app.
The problem with this security check is that it relies on gmail being able to find the caller app, which is only reliable when the caller has asked for result. In your code above, you do not ask for result and therefore gmail does not know who the caller is, and rejects your file.
Since it worked for you in 4.9 but not in 5.0, you know it's not a unix permission problem, so the reason must be the new checks.
TL;DR answer:
replace startActivity with startActivityForResult.
Or better yet, use a content provider.
Use getExternalCacheDir() with File.createTempFile.
Use the following to create a temporary file in the external cache directory:
File tempFile = File.createTempFile("fileName", ".txt", context.getExternalCacheDir());
Then copy your original file's content to tempFile,
FileWriter fw = new FileWriter(tempFile);
FileReader fr = new FileReader(Data.ERR_BAK_FILE);
int c = fr.read();
while (c != -1) {
fw.write(c);
c = fr.read();
}
fr.close();
fw.flush();
fw.close();
now put your file to intent,
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(tempFile));
You should implement a FileProvider, which can create Uris for your app's internal files. Other apps are granted permission to read these Uris. Then, simply instead of calling Uri.fromFile(attachment), you instantiate your FileProvider and use:
fileProvider.getUriForFile(attachment);
Google have an answer for that issue:
Store the data in your own ContentProvider, making sure that other apps have the correct permission to access your provider. The preferred mechanism for providing access is to use per-URI permissions which are temporary and only grant access to the receiving application. An easy way to create a ContentProvider like this is to use the FileProvider helper class.
Use the system MediaStore. The MediaStore is primarily aimed at video, audio and image MIME types, however beginning with Android 3.0 (API level 11) it can also store non-media types (see MediaStore.Files for more info). Files can be inserted into the MediaStore using scanFile() after which a content:// style Uri suitable for sharing is passed to the provided onScanCompleted() callback. Note that once added to the system MediaStore the content is accessible to any app on the device.
Also you can try set permissions for your file:
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
And finally you can copy/store your files in external storage - permissions not needed there.
I tested it and I found out that it was definitely private storage access problem.
When you attach some file to Gmail (over 5.0) do not use the file from private storage such as /data/data/package/. Try to use /storage/sdcard.
You can successfully attach your file.
Not sure why GMail 5.0 doesn't like certain file paths (which I've confirmed it does have read access to), but an apparently better solution is to implement your own ContentProvider class to serve the file. It's actually somewhat simple, and I found a decent example here: http://stephendnicholas.com/archives/974
Be sure to add the tag to your app manifest, and include a "android:grantUriPermissions="true"" within that. You'll also want to implement getType() and return the appropriate MIME type for the file URI, otherwise some apps wont work with this... There's an example of that in the comment section on the link.
I was having this problem and finally found an easy way to send email with attachment. Here is the code
public void SendEmail(){
try {
//saving image
String randomNameOfPic = Calendar.DAY_OF_YEAR+DateFormat.getTimeInstance().toString();
File file = new File(ActivityRecharge.this.getCacheDir(), "slip"+ randomNameOfPic+ ".jpg");
FileOutputStream fOut = new FileOutputStream(file);
myPic.compress(Bitmap.CompressFormat.JPEG, 100, fOut);
fOut.flush();
fOut.close();
file.setReadable(true, false);
//sending email
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_EMAIL, new String[]{"zohabali5#gmail.com"});
intent.putExtra(Intent.EXTRA_SUBJECT, "Recharge Account");
intent.putExtra(Intent.EXTRA_TEXT, "body text");
//Uri uri = Uri.parse("file://" + fileAbsolutePath);
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(file));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(Intent.createChooser(intent, "Send email..."),12);
}catch (Exception e){
Toast.makeText(ActivityRecharge.this,"Unable to open Email intent",Toast.LENGTH_LONG).show();
}
}
In this code "myPic" is bitmap which was returned by camera intent
Step 1: Add authority in your attached URI
Uri uri = FileProvider.getUriForFile(context, ""com.yourpackage", file);
Same as your manifest file provide name
android:authorities="com.yourpackage"
Step 2`; Add flag for allow to read
myIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
My application uses the intent method to send out emails to users, as a convenient way of exporting Excel spreadsheet data (created by the JExcell API).
The file is contained on the SD card in a folder called records.
The file I am attempting to send is call measurments.xls.
I have tested in code for the existence of the file prior to sending. The email composer shows an attachment, but when I send and then receive the email the attachment is not there.
However, if I substitute the excel file for a png image, the attachment is received. So what gives??
Below is the code I use to send out the email, it is simply a paramiterised static method in a class by its self.
public static void sendEmailWithAttachment(Context ctx, String to,String subject, String message, String fileAndLocation)
{
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("text/plain");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] {to});
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subject);
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, message);
File file = new File(fileAndLocation);
// File file = getFileStreamPath();
if (file.exists())
{
Log.v("Farmgraze", "Email file_exists!" );
}
else
{
Log.v("Farmgraze", "Email file does not exist!" );
}
Log.v("FarmGraze", "SEND EMAIL FileUri=" + Uri.parse("file:/"+ fileAndLocation));
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file:/"+ fileAndLocation));
ctx.startActivity(Intent.createChooser(emailIntent, "Send mail..."));
}//end method
So what do I need to do to receive the xls file? Change the mime types in the second line of the method's code? If so what do. Any helpful advice would be greatly appreciated.
Thanks for reading.
A.
Ok folks just to add closure to this question I found the solution.
The problem was that the file path String sent to the URI, needs to have three forward slashes.
As in:
file:///sdcard/somefolder/some_file.xls.
Also for excel documents one needs to set the type as follows:
emailIntent.setType("application/excel");
So the problem was a two pronged one. I was aware of the three slash solution via this thread, but as it did not work thought the problem lie else where.
Also I became aware of the correct mime types via this webpage which lists all of the supported mime types, and may be very useful for other readers.
So thanks for reading and taking an interest in my little problem which is now solved.
I think the issue might be with your mime-type. Try this:
MimeTypeMap mime = MimeTypeMap.getSingleton();
String mimeTypeForXLSFile = mime.getMimeTypeFromExtension(".xls");
emailIntent.setType(mimeTypeForXLSFile);
My application allows users to create and modify files. I would like them to be able to send a file as an email attachment. So, I need to first create and write to a temporary file, which I then attach to the email. And then I would like to delete the temporary file when the email program finishes. Unfortunately, the gmail app responds with a result code as soon as the user clicks "send"; and if I delete the file as soon as the result code is received, no attachment is sent.
Its possible that something else is going wrong and the attachment is not sent for a different reason, but I'm pretty sure my assessment is correct because the below code works properly if I comment out the mEmailTmpFile.delete() call. It also works fine if I do something very undesirable like Thread.sleep(4000) immediately prior to mEmailTmpFile.delete().
Is there anyway to be notified when the email is done sending? Or any other suggestions for how I should work around this?
//send an email...
File externalStorage = Environment.getExternalStorageDirectory();
String sdcardPath = externalStorage.getAbsolutePath();
mEmailTmpFile = new File(sdcardPath + "/" + name );
//do some other to ensure unqiueness and then write to the file...
//all done writing, send email
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("application/zip");
sendIntent.putExtra(Intent.EXTRA_SUBJECT, name);
sendIntent.putExtra(Intent.EXTRA_TEXT, "File attached.");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://"+ mEmailTmpFile.getPath()));
startActivityForResult(Intent.createChooser(sendIntent, "Email"), REQUESTCODE_EMAIL);
public synchronized void onActivityResult(int reqCode, int resultCode, Intent data)
{
if (reqCode == REQUESTCODE_EMAIL)
{
mEmailTmpFile.delete();
}
}
In my apps, I don't delete the temporary file. Android will take care of it by deleting the file if it needs space. I would ensure that you don't create the tmp file in the SDCard root directory since that can look messy but other than that there shouldn't be a problem.
I have a programmatically generated image that I want to send as an attachment via the ACTION_SEND and EXTRA_STREAM method.
But how do i do this?
My first attempt (writing to my context.getCacheDir() based file path) appeared to work in the Gmail preview (no image preview, but attached file name and icon was visible), but the attachment never arrived on the recipient side. I guess this has something to do with permissions on the generated file, but how to avoid this? Do I need to set more permissive settings on these generated files (so that the Gmail activity can access)? Is that even possible for the app's cache folder?
Is there another file location that would be more suitable to write my files to? I considered the downloads folder, but think it would be an awkward location for something that only needs to exist until it has been emailed.
I have even tried encoding my image purely in a data:image/png;base64,ABCD... style URI. This, too, showed up in Gmail preview (attachment icon, but no file name), but did not result in a recipient-side attachment.
Has anyone been able to attach a one-shot generated image to an email intent by any means? What options may I have overlooked?
My problem really consisted of two parts:
context.getCacheDir() is private to your app. You can't put something there and expect another app to be able to access it.
I misunderstood what MIME type I should have been using. Even though I was sending email text, I really needed to specify image/png for the sake of my attachment.
Additionally, research indicated that putting (potentially large) images on the primary memory was not a good idea, even if you were going to immediately clean it up.
Once I did these things and wrote my generated images to a public location on the SD Card, it worked just fine.
So, in overview:
Request SD Card Access in your manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Make sure SD Card is available
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()))
{
//Bail gracefully
}
Create a directory on the SD Card
File pngDir = new File(
Environment.getExternalStorageDirectory(),
//Loose convention inferred from app examples
"Android/data/com.somedomain.someapp/flotsam");
if (!pngDir.exists())
pngDir.mkdirs();
Write your file to that directory and capture the Uri
File pngFile = new File(pngDir, "jetsam.png");
//Save file encoded as PNG
Uri pngUri = Uri.fromFile(pngFile);
Build an ACTION_SEND intent
Intent intent = new Intent(android.content.Intent.ACTION_SEND);
intent.setType("image/png"); //
intent.putExtra(android.content.Intent.EXTRA_EMAIL, new String[] { "someone#somewhere.com" });
intent.putExtra(android.content.Intent.EXTRA_SUBJECT, "Portable Network Graphics");
intent.putExtra(android.content.Intent.EXTRA_CC, new String[] { "carbon#somewhere.com" });
intent.putExtra(Intent.EXTRA_TEXT, "Something textual");
intent.putExtra(Intent.EXTRA_STREAM, pngUri);
And then start the activity
context.startActivity(Intent.createChooser(intent, "Something Pithy"));
And then make sure you clean everything up...
Caveat 1
There appears to be more support coming for app-specific SD Card directories, but alas, not in my required SDK version.
Caveat 2
This is an overview of the solution that eventually worked for me. It is not necessarily a "best practice" approach.
Caveat 3
This does mean that the application has to have an SD Card mounted in order to have the image attachments feature available, but this was totally acceptable for my use case. Your mileage may vary. If the SD Card is not available, I append a friendly note to the email explaining why the images could not be attached and how to rectify the situation.
I've just run into exactly the same issue (wanting to attach a text file in my case). If you look in the Android log, the reason for it is:
02-28 21:01:28.434: E/Gmail(19673): file:// attachment paths must point to file:///mnt/sdcard. Ignoring attachment file:///data/data/com.stephendnicholas.gmailattach/cache/Test.txt
As a workaround (as mentioned by HRJ), you can use a ContentProvider to provide access to files in your application's internal cache so that Gmail can attach them. I've just written up a blog post on how to do it.
Hopefully that's of some help :)
tableLayout.buildDrawingCache();
Bitmap test = Bitmap.createBitmap(tableLayout.getDrawingCache());
tableLayout.destroyDrawingCache();
Log.d("Image", test.toString());
String path = Environment.getExternalStorageDirectory().toString();
Log.d("Path", path);
File file = new File(path,"mail_image.png");
Uri pngUri = Uri.fromFile(file);
Log.d("Real Image Path", pngUri.toString());
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("image/png");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL, "email to");
emailIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,"Subject");
emailIntent.putExtra(android.content.Intent.EXTRA_TEXT, "From My App");
emailIntent.putExtra(android.content.Intent.EXTRA_STREAM, pngUri );
startActivity(Intent.createChooser(emailIntent, "Send mail..."));
I have an Android app that saves a text file directly onto the phone, in the app's install directory. I need to allow the user to create a new email, attaching this saved text file. When I start the intent to send the email, everything shows up in Gmail correctly, but the attachment does not get sent. All of my searches on stack overflow seem to only deal with attaching an image file from the SD card. Below is the code that I used. Please let me know if I have done something incorrectly.
File myFile = new File(getFilesDir() + "/" + "someFile.txt");
FileOutputStream stream = null;
if( file != null )
{
steam = openFileOutput("someFile.txt", Context.MODE_WORLD_READABLE);
stream.write(some_data);
Uri uri = Uri.fromFile(myFile);
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
sendIntent.putExtra(Intent.EXTRA_TEXT, email_text);
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
file.close();
startActivity(Intent.createChooser(sendIntent, "Email:"));
}
I've also tried sendIntent.setType("application/octet-stream"); but that didn't make a difference. I'm at a loss for why the file doesn't attach and get sent.
Any ideas?
I've just run into exactly the same issue (wanting to attach a text file to a Gmail message). If you look in the Android log, the reason for it is:
02-28 21:01:28.434: E/Gmail(19673): file:// attachment paths must point to file:///mnt/sdcard. Ignoring attachment file:///data/data/com.stephendnicholas.gmailattach/cache/Test.txt
As a workaround, you can use a ContentProvider to provide access to files in your application's internal cache so that Gmail can attach them. I've just written up a blog post on how to do it.
Hopefully that's of some help :)
I've seen this before and the only way I could solve it was by writing the file to the SD card.
It's worth trying writing to the file to the SD card and attaching it if only to eliminate the files location as the cause of the problem.