Attach an XLS (Excel) file to Email - android

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);

Related

Gmail 5.0 app fails with "Permission denied for the attachment" when it receives ACTION_SEND intent

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);

unable to attach file to an ACTION_SEND intent

I'm trying to attach a text file to an email and I'm getting a weird error that I hope someone can help me with. It works fine when the user selects the gmail app from the chooser, but if they select the built in mail application, they see a toast that says "Unable to attach file".
The code looks like this:
public static void sendMail(Context context, String emailBody, String emailSubject, String emailAddress, String attachmentFilename){
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND);
emailIntent.setType("plain/text");
emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] { emailAddress});
emailIntent.putExtra(Intent.EXTRA_SUBJECT, emailSubject);
emailIntent.putExtra(Intent.EXTRA_TEXT, emailBody);
if(attachmentFilename != null) {
//Add the attachment by specifying a reference to our custom ContentProvider and the specific file of interest
emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("content://" + Settings.VYPR_LOG_PROVIDER_AUTHORITY + "/" + attachmentFilename));
}
context.startActivity(emailIntent);
}
Anyone have any thoughts on what might be going on here? Most of what I have seen on here has to do with the attachment being on the SD card. I actually didn't write this code myself, but it seems like that must not be the issue here since it does work if the user selects the gmail app rather than the built in one.
Thanks in advance!
I experienced the same problem long time ago. Had to make the use of the Gmail app mandatory to send attachments. I could not figure out why the built-in email app did not work.
If you are trying to receive the attachment to a specific email address, you may also consider deploying a web service to upload the attachment.
Hope it helps.

Sending Sqlite database as email attachment in Android

I have a simple app which I am using to spike sending attachments.
I have managed to send a text file from the SD card (although I couldn't get this to work with a file created in the apps private area using openFileOutput(fileName, 0)) but I now want to send a database.
By debugging I can verify the database exists and has an entry in its only table. My code to send looks like this:
gmailButton = (Button) findViewById(R.id.button);
gmailButton.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_SUBJECT, "subject line");
sendIntent.putExtra(Intent.EXTRA_TEXT,"Body of email");
Uri uri = Uri.fromFile(getDatabasePath("TEST_DB"));
//uri = file:///data/data/com.gmailspike/databases/TEST_DB
sendIntent.putExtra(Intent.EXTRA_STREAM, uri);
sendIntent.setType("application/octet-stream");
startActivity(Intent.createChooser(sendIntent,"Email:"));
}
})
;
However, when the email client opens the attachment has a size of 0 bytes and if I touch to open the attachment the client says the file cannot be found.
Any ideas? I'm not sure the mime type is correct, or even if it is important!
I remember having had an issue like this too, when trying to send an email with an image attachment, linking to a file stored in the apps private data folder. The attachment is just missing.
Using the mail intent opens a new application to handle the mail-creation. Therefore you either need to write a Content Provider to allow other applications access to your private data.
Or copy the content to public area first and add it to the mail intent from there (this only works when a SD-Card is present with most phones). The External Storage.getExternalStorageDirectory() could maybe used in that case.
Hope this helps to find a solution.
As for the MIME type, it is surely important, since an application has to support it (explicitly or through wildcard) to respond to the intent and to be found via createChooser.
I think Gmail for instance accepts */* as MIME type with ACTION_SEND but I don't know about other mail clients.
Edit : And as for the permission, have a look at the FLAG_GRANT_READ_URI_PERMISSION flag from the Intent class : http://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION

Android email SQLite database

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

Android: problem sending email with attachment from my application

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.

Categories

Resources