Sending an email with attachment in Xamarin.Forms for Android and iOS - xamarin

I am creating an app to send an email with an attachment. I have tried using mailTo to do it but it is not working. Are there any other ways to send an email with attachment?
This is the code that I have tried so far
private void Button_Clicked_1(object sender, EventArgs e)
{
string toEmail = "toemail#nyp.edu.sg";
string emailSubject = "Test Email";
string emailBody = "Email Body";
string attachment = "C:/Users/L30901/Desktop/download.jpg";
Device.OpenUri(new Uri(String.Format("mailto:{0}?subject={1}&body={2}&attachment=file:///{3}", toEmail, emailSubject, emailBody, attachment)));
}
This is what I did for iOS
MainPage.xaml.cs
private void Button_Clicked_1(object sender, EventArgs e)
{
DependencyService.Get<IDependency>().SendEmail();
}
IDependency.cs
public interface IDependency
{
void SendEmail();
}
IOSEmail.cs
[assembly: Dependency(typeof(IOSEmail))]
namespace Notification.iOS
{
public class IOSEmail : IDependency
{
MFMailComposeViewController mailController;
public void SendEmail()
{
if (MFMailComposeViewController.CanSendMail)
{
mailController = new MFMailComposeViewController();
mailController.SetToRecipients(new string[] { "some.person#somewhere.com" });
mailController.SetSubject("Testing");
mailController.SetMessageBody("See attached file", false);
NSData data = NSData.FromFile(pdffilename);
mailController.AddAttachmentData(data, "application/jpeg", "xxxxx.jpg");// For JPEG
mailController.Finished += HandleMailFinished;
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(mailController, true, null);
}
}
private void HandleMailFinished(object sender, MFComposeResultEventArgs e)
{
e.Controller.DismissViewController(true, null);
}

What you need to do can only be done using Xamarin.Forms DependencyService:
For Android you need to do something like this:
Get the Directory of your attachment using Java.IO:
Java.IO.File dir = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
Note: I have used the downloads directory you can change it as per your need at any point in time.
Once you are done with picking that directory using Android OS environment then pick the file something like this;
Java.IO.File file = new Java.IO.File(dir, "yourFile.pdf"); //This could be any other type of file aswell
Get the URI of that Android file something like this:
Android.Net.Uri path = Android.Net.Uri.FromFile(file);
Create the Intent to send the data from to your Emailing app something like this:
var email = new Intent(Android.Content.Intent.ActionSend);
email.PutExtra(Android.Content.Intent.ExtraEmail,
new string[] { "some.person#somewhere.com" }); // to whom you want to send the email
email.PutExtra(Android.Content.Intent.ExtraCc,
new string[] { "some.person#somewhereelse.com" }); // to whom you want to Copy in the email (CC)
email.PutExtra(Android.Content.Intent.ExtraSubject, "Awesome File");
email.PutExtra(Android.Content.Intent.ExtraText,
"See attached file");
email.PutExtra(Intent.ExtraStream, path);
email.SetType("message/rfc822");
StartActivity(email);
For IOS you need to do something like this:
Create an instance of MFMailComposeViewController and add a subject and message body to it;
var mailer= new MFMailComposeViewController();
mailer.SetSubject("xxxx");
mailer.SetMessageBody("", true);
mailer.Finished += HandleMailFinished; //This controllers finished event in case you need it
Now it is mandatory that you have your file in the document directory for it to be available to the application for more information check Working with the File System
Assuming that you read that and you have added the file to the document directory of your application get the NSData for your attachment:
NSData data = NSData.FromFile(pdfFileName);
mailer.AddAttachmentData(data, "application/pdf", "xxxxx.pdf");// For PDF
mail.AddAttachmentData(csvdata, "application/csv", "csv.txt"); // For CSV/TXT
Get the currently displayed Viewcontroller something like this:
public UIViewController GetCurrentUIController()
{
UIViewController viewController;
var window = UIApplication.SharedApplication.KeyWindow;
if (window == null)
{
return null;
}
if (window.RootViewController.PresentedViewController == null)
{
window = UIApplication.SharedApplication.Windows
.First(i => i.RootViewController != null &&
i.RootViewController.GetType().FullName
.Contains(typeof(Xamarin.Forms.Platform.iOS.Platform).FullName));
}
viewController = window.RootViewController;
while (viewController.PresentedViewController != null)
{
viewController = viewController.PresentedViewController;
}
return viewController;
}
Then Present the Viewcontroller something like this:
var _CurrentViewComtroller= GetCurrentUIController();
_CurrentViewComtroller?.PresentViewController(mailer, true, null);
The Handled Finish method would be something like this:
private void HandleMailFinished(object sender, MFComposeResultEventArgs args)
{
DismissViewController (true, null);
}

Related

Get and Display path of Chosen photo from gallery in Xamarin forms

I am using a Dependency service to pick a photo from the gallery. and I want to show the path when the user selects an image from their phone in a Label.
I have read too many logs but not getting the proper results.
I want it like this:
Now the selected image is displayed properly but what I don't get is how to display the path of the selected image.
Please suggest me how to do it for both android and ios.
Note: I'm using Dependency service for it so I don't want third-party plugins.
I hope I will get a better solution for this.
Thanks in advance.
Creating the interface in forms
namespace xxx
{
public interface IPhotoPickerService
{
Task<Dictionary<string,Stream>> GetImageStreamAsync();
}
}
in iOS
[assembly: Dependency (typeof (PhotoPickerService))]
namespace xxx.iOS
{
public class PhotoPickerService : IPhotoPickerService
{
TaskCompletionSource<Dictionary<string, Stream>> taskCompletionSource;
UIImagePickerController imagePicker;
Task<Dictionary<string, Stream>> IPhotoPickerService.GetImageStreamAsync()
{
// Create and define UIImagePickerController
imagePicker = new UIImagePickerController
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary)
};
// Set event handlers
imagePicker.FinishedPickingMedia += OnImagePickerFinishedPickingMedia;
imagePicker.Canceled += OnImagePickerCancelled;
// Present UIImagePickerController;
UIWindow window = UIApplication.SharedApplication.KeyWindow;
var viewController = window.RootViewController;
viewController.PresentModalViewController(imagePicker, true);
// Return Task object
taskCompletionSource = new TaskCompletionSource<Dictionary<string, Stream>>();
return taskCompletionSource.Task;
}
void OnImagePickerFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs args)
{
UIImage image = args.EditedImage ?? args.OriginalImage;
if (image != null)
{
// Convert UIImage to .NET Stream object
NSData data;
if (args.ReferenceUrl.PathExtension.Equals("PNG") || args.ReferenceUrl.PathExtension.Equals("png"))
{
data = image.AsPNG();
}
else
{
data = image.AsJPEG(1);
}
Stream stream = data.AsStream();
UnregisterEventHandlers();
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(args.ImageUrl.ToString(), stream);
// Set the Stream as the completion of the Task
taskCompletionSource.SetResult(dic);
}
else
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
}
imagePicker.DismissModalViewController(true);
}
void OnImagePickerCancelled(object sender, EventArgs args)
{
UnregisterEventHandlers();
taskCompletionSource.SetResult(null);
imagePicker.DismissModalViewController(true);
}
void UnregisterEventHandlers()
{
imagePicker.FinishedPickingMedia -= OnImagePickerFinishedPickingMedia;
imagePicker.Canceled -= OnImagePickerCancelled;
}
}
}
in Android
in MainActivity
public class MainActivity : FormsAppCompatActivity
{
...
// Field, property, and method for Picture Picker
public static readonly int PickImageId = 1000;
public TaskCompletionSource<Dictionary<string,Stream>> PickImageTaskCompletionSource { set; get; }
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);
if (requestCode == PickImageId)
{
if ((resultCode == Result.Ok) && (intent != null))
{
Android.Net.Uri uri = intent.Data;
Stream stream = ContentResolver.OpenInputStream(uri);
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.ToString(), stream);
// Set the Stream as the completion of the Task
PickImageTaskCompletionSource.SetResult(dic);
}
else
{
PickImageTaskCompletionSource.SetResult(null);
}
}
}
}
[assembly: Dependency(typeof(PhotoPickerService))]
namespace xxx.Droid
{
public class PhotoPickerService : IPhotoPickerService
{
public Task<Dictionary<string,Stream>> GetImageStreamAsync()
{
// Define the Intent for getting images
Intent intent = new Intent();
intent.SetType("image/*");
intent.SetAction(Intent.ActionGetContent);
// Start the picture-picker activity (resumes in MainActivity.cs)
MainActivity.Instance.StartActivityForResult(
Intent.CreateChooser(intent, "Select Picture"),
MainActivity.PickImageId);
// Save the TaskCompletionSource object as a MainActivity property
MainActivity.Instance.PickImageTaskCompletionSource = new TaskCompletionSource<Dictionary<string,Stream>>();
// Return Task object
return MainActivity.Instance.PickImageTaskCompletionSource.Task;
}
}
}
invoke it
Dictionary<string, Stream> dic = await DependencyService.Get<IPhotoPickerService>().GetImageStreamAsync();
Stream stream;
string path;
foreach ( KeyValuePair<string, Stream> currentImage in dic )
{
stream = currentImage.Value;
path = currentImage.Key;
label.Text = path;
if (stream != null)
{
image.Source = ImageSource.FromStream(() => stream);
}
}
Update
If you want to get the path , you could invoke
Dictionary<string, Stream> dic = new Dictionary<string, Stream>();
dic.Add(uri.Path, stream);

How to share contact data of my app with whtsapp in .vcf /vcard format using xamarin forms

I have an application that creates a .vcf file after you input data. it stores storage provided by the phone, I want to click on a list view item and get the vcf file and share it via Email, SMS, WhatsApp, Skype etc how do I implement this in IOS and Android.
Thank you
I have got the answer of creating .vcf file which is given below. and for share that file follow this link : https://github.com/adamped/ShareDialog
private void Share_Clicked(object sender, EventArgs e)
{
try
{
var _btn = sender as Button;
var record = _btn.BindingContext as Contact;
int tempcontactID = record.ContactID;
if (record.CardFrontImage == null)
{
record.CardImage = record.CardBackImage;
}
else
{
record.CardImage = record.CardFrontImage;
}
string baseimage = Convert.ToBase64String(record.CardImage);
var vcf = new StringBuilder(); //vcf code start
vcf.AppendLine("BEGIN:VCARD");
vcf.AppendLine("VERSION:3.0");
vcf.AppendLine($"N:{record.ContactName};{string.Empty}; ;;");
vcf.AppendLine($"FN:{record.ContactName}");
vcf.AppendLine($"ORG:{record.CompanyName}");
vcf.AppendLine($"TITLE:{record.Designation}");
vcf.AppendLine($"PHOTO;ENCODING=BASE64;TYPE=PNG:{baseimage}");
vcf.AppendLine($"TEL;TYPE=work,voice;VALUE=uri:tel:{record.PhoneNumber}");
vcf.AppendLine("END:VCARD");
string fileName = Path.Combine("/storage/emulated/0/Android/data/com.Gamma.GammaNetworkingApp/files/", record.ContactID + record.ContactName + ".vcf");
using (var writer = new StreamWriter(fileName))
{
writer.Write(vcf.ToString());
}
string text = File.ReadAllText(fileName);
bool doesExist = File.Exists(fileName);
if (doesExist == true)
{
var share = DependencyService.Get<IShare>();
share.Show("Contact share", record.ContactName, fileName);
}
}
catch (Exception ex)
{
string test = ex.ToString();
Navigation.PushAsync(new HomePage());
}
}

store voice file path android xamarin

the following code is what I use to grab a voice file to send an email as an attachment. However, I am not able to find this file. no clue where it is stored. (this is what I see when I hover the path /storage/emulated/0/test.mp4). the storage folder is empty even when this run, for this reason, the attachment can't be sent. any ideas? Thank you! updated code
string path = "";
public Recorder_Droid()
{
var sqlliteFilname = "test.mp4";
string filePath = global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
path = Path.Combine(filePath, sqlliteFilname);
_recorder = new MediaRecorder();
_player = new MediaPlayer();
_player.Completion += (sender, e) => {
_player.Reset();
};
}
MediaRecorder _recorder;
MediaPlayer _player;
public void PlayAudio()
{
if (File.Exists(path))
{
File.Delete(path);
}
if (_recorder == null)
{
_recorder = new MediaRecorder();
}
_recorder.Reset();
_recorder.SetAudioSource(AudioSource.Mic);
_recorder.SetOutputFormat(OutputFormat.Mpeg4);
_recorder.SetAudioEncoder(AudioEncoder.Aac);
_recorder.SetOutputFile(path);
_recorder.Prepare(); // Prepared state
_recorder.Start(); // Recording state.
return;

Xamarin.Forms Xamarin Android

SaveFileDialog in XamarinForms
My requirement is to save a file in Xamarin.Forms like below image and what ever I tried so far is:
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged (e);
if (e.NewElement != null)
{
var customWebView = Element as CustomWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
string root = Android.OS.Environment.ExternalStorageDirectory.ToString();
// Java.IO.File myDir = new Java.IO.File(root + "/WingsPdfs");
// myDir.Mkdir();
string path = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
Java.IO.File file = new Java.IO.File(path, "WingsPdfGenerated.pdf");
FileOutputStream outs = new FileOutputStream(file);
outs.Write(customWebView.PdfStream.ToArray());
outs.Flush();
outs.Close();
try
{
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", file));
}
catch(Exception ex)
{
}
}
}
}
In the above code I hardcoded the location of the file but I want user to select particular location and save to the selected location.
Thank You
enter image description here

UIDocumentPickerViewController selection in Xamarin Forms

I'm stuck on getting my DocumentPicker fully working. Right now it presents the view controller but I can't figure out how to wait or get the result.
In swift you just write the void documentPicker(UIDocumentPickerViewController controller, didPickDocumentAtUrl... method and when it's finished it goes to there.
But in Xamarin it must not be that simple. I've written that method, from the class I'm calling it from as well as in my AppDelegate.cs class and as well as in my Main.cs class. None seem to work, unless I've written it wrong.
What I have is this ....
public async Task<string> pickResume()
{
string path = string.Empty;
var controller = new UIViewController();
var docVC = new UIDocumentPickerViewController(new string[] { "org.openxmlformats.wordprocessingml.document", "com.microsoft.word.doc" }, UIDocumentPickerMode.Import);
UIViewController topController = getTopViewController();
topController.PresentViewController(docVC, true, null);
return path;
}
void documentPicker(UIDocumentPickerViewController controller, NSUrl didPickDocumentAtURL)
{
Console.WriteLine("done");
}
getTopViewController() is just a helper method to get the top view controller so I can present the DocumentPicker
Figured it out, and it's a lot easier than I was making it out to be.
The UIDocumentPickerViewController has two EventHandlers, DidPickDocument and WasCancelled so I just assigned those to two different methods and done.
public async Task<string> pickResume()
{
string path = string.Empty;
var controller = new UIViewController();
var docVC = new UIDocumentPickerViewController(new string[] { "org.openxmlformats.wordprocessingml.document", "com.microsoft.word.doc" }, UIDocumentPickerMode.Import);
docVC.DidPickDocument += DocVC_DidPickDocument;
docVC.WasCancelled += DocVC_WasCancelled;
UIViewController topController = getTopViewController();
topController.PresentViewController(docVC, true, null);
return await GetDocPath(new CancellationTokenSource());
}
private void DocVC_WasCancelled(object sender, EventArgs e)
{
//Handle being cancelled
}
private void DocVC_DidPickDocument(object sender, UIDocumentPickedEventArgs e)
{
//Handle document selection
}

Resources