File: Share\Share.shared.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Maui.Graphics;
using Microsoft.Maui.Storage;
 
namespace Microsoft.Maui.ApplicationModel.DataTransfer
{
	/// <summary>
	/// The Share API enables an application to share data such as text and web links to other applications on the device.
	/// </summary>
	public interface IShare
	{
		/// <summary>
		/// Show the operating systems user interface to share text.
		/// </summary>
		/// <param name="request">A <see cref="ShareTextRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		Task RequestAsync(ShareTextRequest request);
 
		/// <summary>
		/// Show the operating system's user interface to share a single file.
		/// </summary>
		/// <param name="request">A <see cref="ShareFileRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		Task RequestAsync(ShareFileRequest request);
 
		/// <summary>
		/// Show the operating system's user interface to share multiple files.
		/// </summary>
		/// <param name="request">A <see cref="ShareMultipleFilesRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		Task RequestAsync(ShareMultipleFilesRequest request);
	}
 
	/// <summary>
	/// The Share API enables an application to share data such as text and web links to other applications on the device.
	/// </summary>
	public static partial class Share
	{
		/// <summary>
		/// Show the operating system's user interface to share text.
		/// </summary>
		/// <param name="text">The text to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(string text) =>
			Current.RequestAsync(text);
 
		/// <summary>
		/// Show the operating system's user interface to share text.
		/// </summary>
		/// <param name="text">The text to share.</param>
		/// <param name="title">The title to display on the operating system share dialog.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(string text, string title) =>
			Current.RequestAsync(text, title);
 
		/// <summary>
		/// Show the operating system's user interface to share text.
		/// </summary>
		/// <param name="request">A <see cref="ShareTextRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(ShareTextRequest request) =>
			Current.RequestAsync(request);
 
		/// <summary>
		/// Show the operating system's user interface to share a single file.
		/// </summary>
		/// <param name="request">A <see cref="ShareFileRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(ShareFileRequest request) =>
			Current.RequestAsync(request);
 
		/// <summary>
		/// Show the operating system's user interface to share multiple files.
		/// </summary>
		/// <param name="request">A <see cref="ShareMultipleFilesRequest"/> object containing the details of the data to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(ShareMultipleFilesRequest request) =>
			Current.RequestAsync(request);
 
		static IShare Current => ApplicationModel.DataTransfer.Share.Default;
 
		static IShare? defaultImplementation;
 
		/// <summary>
		/// Provides the default implementation for static usage of this API.
		/// </summary>
		public static IShare Default =>
			defaultImplementation ??= new ShareImplementation();
 
		internal static void SetDefault(IShare? implementation) =>
			defaultImplementation = implementation;
	}
 
 
	/// <summary>
	/// Concrete implementation of the <see cref="IShare"/> APIs.
	/// </summary>
	partial class ShareImplementation : IShare
	{
		/// <inheritdoc cref="IShare.RequestAsync(ShareTextRequest)"/>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
		/// <exception cref="ArgumentException">Thrown when <see cref="ShareTextRequest.Text"/> and <see cref="ShareTextRequest.Uri"/> are both <see langword="null"/> or empty.</exception>
		public Task RequestAsync(ShareTextRequest request)
		{
			if (request == null)
				throw new ArgumentNullException(nameof(request));
 
			if (string.IsNullOrEmpty(request.Text) && string.IsNullOrEmpty(request.Uri))
				throw new ArgumentException($"Both the {nameof(request.Text)} and {nameof(request.Uri)} are invalid. Make sure to include at least one of them in the request.");
 
			return PlatformRequestAsync(request);
		}
 
		/// <inheritdoc cref="IShare.RequestAsync(ShareFileRequest)"/>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
		/// <exception cref="ArgumentException">Thrown when <see cref="ShareFileRequest.File"/> is <see langword="null"/>.</exception>
		public Task RequestAsync(ShareFileRequest request)
		{
			if (request == null)
				throw new ArgumentNullException(nameof(request));
 
			if (request.File == null)
				throw new ArgumentException(FileNullException(nameof(request.File)));
 
			return PlatformRequestAsync(request);
		}
 
		/// <inheritdoc cref="IShare.RequestAsync(ShareMultipleFilesRequest)"/>
		/// <exception cref="ArgumentNullException">Thrown when <paramref name="request"/> is <see langword="null"/>.</exception>
		/// <exception cref="ArgumentException">Thrown when <see cref="ShareMultipleFilesRequest.Files"/> is <see langword="null"/> or empty or one of the entries in <see cref="ShareMultipleFilesRequest.Files"/> is <see langword="null"/>.</exception>
		public Task RequestAsync(ShareMultipleFilesRequest request)
		{
			if (request == null)
				throw new ArgumentNullException(nameof(request));
 
			if (!(request.Files?.Count > 0))
				throw new ArgumentException(FileNullException(nameof(request.Files)));
 
			if (request.Files.Any(file => file == null))
				throw new ArgumentException(FileNullException(nameof(request.Files)));
 
			return PlatformRequestAsync(request);
		}
 
		static string FileNullException(string file)
			=> $"The {file} parameter in the request files is invalid";
	}
 
	/// <summary>
	/// Static class with extension methods for the <see cref="IShare"/> APIs.
	/// </summary>
	public static class ShareExtensions
	{
		/// <summary>
		/// Show the operating system's user interface to share text.
		/// </summary>
		/// <param name="share">The object this method is invoked on.</param>
		/// <param name="text">The text to share.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(this IShare share, string text) =>
			share.RequestAsync(new ShareTextRequest(text));
 
		/// <summary>
		/// Show the operating systems user interface to share text.
		/// </summary>
		/// <param name="share">The object this method is invoked on.</param>
		/// <param name="text">The text to share.</param>
		/// <param name="title">The title to display on the operating system share dialog.</param>
		/// <returns>A <see cref="Task"/> object with the current status of the asynchronous operation.</returns>
		public static Task RequestAsync(this IShare share, string text, string title) =>
			share.RequestAsync(new ShareTextRequest(text, title));
	}
 
	/// <summary>
	/// Base class for the different share requests that can be used with the <see cref="IShare"/> API.
	/// </summary>
	public abstract class ShareRequestBase
	{
		/// <summary>
		/// Gets or sets the title to use on the operating system's share user interface.
		/// </summary>
		public string? Title { get; set; }
 
		/// <summary>
		/// Gets or sets the source rectangle to display the iOS share user interface from.
		/// </summary>
		/// <remarks>This functionality is only available on iOS on an iPad.</remarks>
		public Rect PresentationSourceBounds { get; set; } = Rect.Zero;
	}
 
	/// <summary>
	/// Represents a request for sharing text with other apps on the device.
	/// </summary>
	public class ShareTextRequest : ShareRequestBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareTextRequest"/> class.
		/// </summary>
		public ShareTextRequest()
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareTextRequest"/> class with the given text.
		/// </summary>
		/// <param name="text">The text to share.</param>
		public ShareTextRequest(string text) =>
			Text = text;
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareTextRequest"/> class with the given text and title.
		/// </summary>
		/// <param name="text">The text to share.</param>
		/// <param name="title">The title to display on the operating systems share dialog.</param>
		public ShareTextRequest(string text, string title)
			: this(text) => Title = title;
 
		/// <summary>
		/// Gets or sets the main text or message to share.
		/// </summary>
		public string? Text { get; set; }
 
		/// <summary>
		/// Gets or sets the subject that is sometimes used for applications such as mail clients.
		/// </summary>
		/// <remarks>The subject is only applicable on Android and for certain apps.</remarks>
		public string? Subject { get; set; }
 
		/// <summary>
		/// Gets or sets a valid URI to share.
		/// </summary>
		/// <remarks>Needs to be a valid URI, else an exception may be thrown when sharing.</remarks>
		public string? Uri { get; set; }
	}
 
	/// <summary>
	/// Represents a request for sharing a single file with other apps on the device.
	/// </summary>
	public class ShareFileRequest : ShareRequestBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFileRequest"/> class.
		/// </summary>
		public ShareFileRequest()
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFileRequest"/> class with the given title and file.
		/// </summary>
		/// <param name="title">The title to display on the operating systems share dialog.</param>
		/// <param name="file">The file to share.</param>
		public ShareFileRequest(string title, ShareFile file)
		{
			Title = title;
			File = file;
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFileRequest"/> class with the given title and file.
		/// </summary>
		/// <param name="title">The title to display on the operating systems share dialog.</param>
		/// <param name="file">The file to share.</param>
		public ShareFileRequest(string title, FileBase file)
		{
			Title = title;
			File = new ShareFile(file);
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFileRequest"/> class with the given file.
		/// </summary>
		/// <param name="file">The file to share.</param>
		public ShareFileRequest(ShareFile file)
			=> File = file;
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFileRequest"/> class with the given file.
		/// </summary>
		/// <param name="file">The file to share.</param>
		public ShareFileRequest(FileBase file)
			=> File = new ShareFile(file);
 
		/// <summary>
		/// Gets or sets the file to share.
		/// </summary>
		public ShareFile? File { get; set; }
	}
 
	/// <summary>
	/// Represents a request for sharing multiple files with other apps on the device.
	/// </summary>
	public class ShareMultipleFilesRequest : ShareRequestBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareMultipleFilesRequest"/> class.
		/// </summary>
		public ShareMultipleFilesRequest()
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareMultipleFilesRequest"/> class with the given files.
		/// </summary>
		/// <param name="files">A collection of files to share.</param>
		public ShareMultipleFilesRequest(IEnumerable<ShareFile> files) =>
			Files = files.ToList();
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareMultipleFilesRequest"/> class with the given files.
		/// </summary>
		/// <param name="files">A collection of files to share.</param>
		public ShareMultipleFilesRequest(IEnumerable<FileBase> files)
			: this(ConvertList(files))
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareMultipleFilesRequest"/> class with the given title and files.
		/// </summary>
		/// <param name="title">The title to display on the operating systems share dialog.</param>
		/// <param name="files">A collection of files to share.</param>
		public ShareMultipleFilesRequest(string title, IEnumerable<ShareFile> files)
			: this(files) => Title = title;
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareMultipleFilesRequest"/> class with the given title and files.
		/// </summary>
		/// <param name="title">The title to display on the operating systems share dialog.</param>
		/// <param name="files">A collection of files to share.</param>
		public ShareMultipleFilesRequest(string title, IEnumerable<FileBase> files)
			: this(title, ConvertList(files))
		{
		}
 
		/// <summary>
		/// Gets or sets the files to share.
		/// </summary>
		public List<ShareFile>? Files { get; set; } = new List<ShareFile>();
 
		/// <summary>
		/// Convert a single file share request into a multi-file share request.
		/// </summary>
		/// <param name="request">The <see cref="ShareFileRequest"/> object to convert into a <see cref="ShareMultipleFilesRequest"/> object.</param>
		public static explicit operator ShareMultipleFilesRequest(ShareFileRequest request) =>
			new ShareMultipleFilesRequest
			{
				Title = request.Title,
				Files = new List<ShareFile> { request.File! },
				PresentationSourceBounds = request.PresentationSourceBounds
			};
 
		static IEnumerable<ShareFile> ConvertList(IEnumerable<FileBase> files)
			=> files?.Select(file => new ShareFile(file)) ?? Array.Empty<ShareFile>();
	}
 
	/// <summary>
	/// A representation of a file that can be used with the <see cref="IShare"/> API.
	/// </summary>
	public class ShareFile : FileBase
	{
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFile"/> class with the given file path.
		/// </summary>
		/// <param name="fullPath">The full path to the file represented by this object.</param>
		public ShareFile(string fullPath)
			: base(fullPath)
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFile"/> class with the given file path and content type.
		/// </summary>
		/// <param name="fullPath">The full path to the file represented by this object.</param>
		/// <param name="contentType">Explicit content type (MIME type) of the file (eg: <c>image/png</c>).</param>
		public ShareFile(string fullPath, string contentType)
			: base(fullPath, contentType)
		{
		}
 
		/// <summary>
		/// Initializes a new instance of the <see cref="ShareFile"/> class from the given <see cref="FileBase"/> object.
		/// </summary>
		/// <param name="file">A <see cref="FileBase"/> object that will be wrapped in a <see cref="ShareFile"/> object.</param>
		public ShareFile(FileBase file)
			: base(file)
		{
		}
	}
}