Share Dialog with Xamarin Forms

When your mobile app creates or receives its own content, in the form of a file, you may want to share it. This could include sharing with, Facebook, Email, SMS or any other app that has registered support for the file type. For example PDF readers can handle a PDF, or Facebook and other apps would be able to share image files.

Share Dialog

The Share Dialog is native to each platform, and will display differently on each. Share Dialog is also a name I just created. The actual name is different on each platform. To activate the in-built share dialog, we need to implement this on each platform. First, lets create an interface, so we can call this from our portable library.

public interface IShare
{
    Task Show(string title, string message, string filePath);
}

If you are unfamiliar with how to inject platform specific classes into a portable library, take a look at Dependency Injection.

iOS

iOS calls this an Activity View. Create a UIActivityViewController, fill with the relevant details and display.

public class Share : IShare
{
    // MUST BE CALLED FROM THE UI THREAD
    public async Task Show(string title, string message, string filePath)
    {
        var items = new NSObject[] { NSObject.FromObject(title), NSUrl.FromFilename(filePath) };
	var activityController = new UIActivityViewController(items, null);
	var vc = GetVisibleViewController();

	NSString[] excludedActivityTypes = null;

	if (excludedActivityTypes != null && excludedActivityTypes.Length > 0)
	    activityController.ExcludedActivityTypes = excludedActivityTypes;

	if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
	{
	    if (activityController.PopoverPresentationController != null)
	    {
	        activityController.PopoverPresentationController.SourceView = vc.View;
	    }
	}
	await vc.PresentViewControllerAsync(activityController, true);		
    }

    UIViewController GetVisibleViewController()
    {
        var rootController = UIApplication.SharedApplication.KeyWindow.RootViewController;

	if (rootController.PresentedViewController == null)
	    return rootController;

	if (rootController.PresentedViewController is UINavigationController)
	{
	    return ((UINavigationController)rootController.PresentedViewController).TopViewController;
	}

	if (rootController.PresentedViewController is UITabBarController)
	{
            return ((UITabBarController)rootController.PresentedViewController).SelectedViewController;
	}

	return rootController.PresentedViewController;
    }
}

Android

In Android, we call this an Intent Chooser. Simply create a new Intent, with the FilePath and other relevant information, and call StartActivity.

public class Share: IShare
{
    private readonly Context _context;
    public Share()
    {
        _context = Android.App.Application.Context;
    }

    public Task Show(string title, string message, string filePath)
    {
        var extension = filePath.Substring(filePath.LastIndexOf(".") + 1).ToLower();
        var contentType = string.Empty;

        // You can manually map more ContentTypes here if you want.
        switch (extension)
        {
            case "pdf":
                contentType = "application/pdf";
                break;
            case "png":
                contentType = "image/png";
                break;
            default:
                contentType = "application/octetstream";
                break;
        }

        var intent = new Intent(Intent.ActionSend);
        intent.SetType(contentType);
        intent.PutExtra(Intent.ExtraStream, Uri.Parse("file://" + filePath));
        intent.PutExtra(Intent.ExtraText, string.Empty);
        intent.PutExtra(Intent.ExtraSubject, message ?? string.Empty);

        var chooserIntent = Intent.CreateChooser(intent, title ?? string.Empty);
        chooserIntent.SetFlags(ActivityFlags.ClearTop);
        chooserIntent.SetFlags(ActivityFlags.NewTask);
        _context.StartActivity(chooserIntent);

        return Task.FromResult(true);
    }
}

UWP

UWP calls this a ShareUI. Details of the share have to be stored, to then be read when the ShareTextHandler is called, after you call the method DataTransferManager.ShowShareUI().

public class Share : IShare
{
    private string _filePath;
    private string _title;
    private string _message;
    private readonly DataTransferManager _dataTransferManager;

    public Share()
    {
        _dataTransferManager = DataTransferManager.GetForCurrentView();
        _dataTransferManager.DataRequested += new TypedEventHandler<DataTransferManager, DataRequestedEventArgs>(ShareTextHandler);
    }

    // MUST BE CALLED FROM THE UI THREAD
    public Task Show(string title, string message, string filePath)
    {
        _title = title ?? string.Empty;
        _filePath = filePath;
        _message = message ?? string.Empty;
			
        DataTransferManager.ShowShareUI();
			
        return Task.FromResult(true);
    }

    private async void ShareTextHandler(DataTransferManager sender, DataRequestedEventArgs e)
    {
        DataRequest request = e.Request;

	// Title is mandatory
	request.Data.Properties.Title = string.IsNullOrEmpty(_title) ? Windows.ApplicationModel.Package.Current.DisplayName : _title;

	DataRequestDeferral deferral = request.GetDeferral();

	try
	{
		if (!string.IsNullOrWhiteSpace(_filePath))
		{
			StorageFile attachment = await StorageFile.GetFileFromPathAsync(_filePath);
			List storageItems = new List();
			storageItems.Add(attachment);
			request.Data.SetStorageItems(storageItems);
		}
		request.Data.SetText(_message ?? string.Empty);
	}
	finally
	{
		deferral.Complete();
	}
    }
}

Learn More

If you want to see it in action, have a look at my ShareDialog Repo on GitHub. It will read an image file from storage, and try to share it.


Posted

in

by

Tags: