Thursday 6 August 2015

Xamarin Forms - Mail service for Android - Send email with multiple attachments

Done number of research, testing, refactoring, etc. TBH, It was quite hard to get it work correctly.
Here are the list of websites that I've been through before I got it work, but none of them not really solve my problem... :(
https://gist.github.com/prashantvc/5213961
http://stackoverflow.com/questions/6972210/android-email-sqlite-database
http://stackoverflow.com/questions/23847561/send-email-with-html-content

How to send email from Android with Attachment? or with multiple attachments? 

A long my development and testing, I have figured out:

1. Intent ActionSend
If you use Intent.ActionSend, then you will basically allow to send an email with 1 attachment only.
var intent = new Intent(Intent.ActionSend);

This work well with:
intent.PutExtra(Intent.ExtraStream,  Android.Net.Uri.FromFile (fileIn)  );


2.Intent.ActionSendMultiple
However, if you prefer to send multiple attachments, then you need this:
var intent = new Intent(Intent.ActionSendMultiple); 

This will not work with
intent.PutExtra(Intent.ExtraStream,  Android.Net.Uri.FromFile (fileIn)  );

You need:
intent.PutParcelableArrayListExtra(Intent.ExtraStream, new List<IParcelable>(uris)); 

3. And,...
If you hit the problem where .... you file path is correct, but unable to attach file to your email client (such as Ms Outlook or gmail),. then you need this:
File fileIn = new File(file);
fileIn.SetReadable(true, false); 

The mail activity might not enough rights to read your file. Try to add myFile.setReadable(true, false) before adding to Attachments array.


Here is my example, it has been written as dependencies service for Xamarin.Forms:

[assembly: DependencyAttribute(typeof(MailServiceDroid))]
namespace Jeff.Droid
{
    public class MailServiceDroid : IMailService
    {
        public bool CanSend
        {
            get { return true; }
        }

        public void ShowDraft(string subject, string body, bool html, string to, MessageOrigin origin, IEnumerable<string> attachments = null)
        {
            ShowDraft (subject, body, html, new[] { to }, new string[] { }, new string[] { }, origin, attachments);
        }

        public void ShowDraft(string subject, string body, bool html, string[] to, string[] cc, string[] bcc, MessageOrigin origin, IEnumerable<string> attachments = null)
        {
            //var intent = new Intent(Intent.ActionSend); // This is for single attachment.
            var intent = new Intent(Intent.ActionSendMultiple);

            //intent.SetType ("message/rfc822"); //Set the type according to the attachment type. 
            intent.SetType("application/octet-stream"); // db3
            intent.PutExtra(Intent.ExtraEmail, to);
            intent.PutExtra(Intent.ExtraCc, cc);
            intent.PutExtra(Intent.ExtraBcc, bcc);
            intent.PutExtra(Intent.ExtraSubject, subject ?? string.Empty);

            if (html)
            {
                intent.PutExtra(Intent.ExtraText, Android.Text.Html.FromHtml(body));
            }
            else
            {
                IList<string> x = new List<string>();
                intent.PutStringArrayListExtra(Intent.ExtraText, x);
            }

            if (attachments != null) {
                IntentExtensions.AddAttachments (intent, attachments);
            }

            //Forms.Context.StartActivity (intent); //
            Forms.Context.StartActivity(Intent.CreateChooser(intent, "Send mail..."));
        }
    }


    public static class IntentExtensions
    {
        public static void AddAttachments(this Intent intent, IEnumerable<string> attachments)
        {
            var uris = new List<IParcelable>();
            foreach (var file in attachments)
            {
                File fileIn = new File(file);
                fileIn.SetReadable(true, false); 
                if (fileIn.CanWrite ()) {
                    Android.Net.Uri u = Android.Net.Uri.FromFile (fileIn);
                    uris.Add (u);


                    //This work for: //var intent = new Intent(Intent.ActionSend);
                    //intent.PutExtra(Intent.ExtraStream, u);
                }
            }
            intent.PutParcelableArrayListExtra(Intent.ExtraStream, new List<IParcelable>(uris));
        }

    }
}



Give me a 'like' if you this help! /Jeff

  

How to run unit test for your Xamarin Application in AppCenter?

How to run unit test for your Xamarin application in AppCenter?  When we talk about Building and Distributing your Xamarin app, you m...