File: FilePicker\FilePicker.shared.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
#nullable enable
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Maui.Devices;
using Microsoft.Maui.Storage;
 
namespace Microsoft.Maui.Storage
{
	/// <summary>
	/// Lets the user pick a file from the device's storage.
	/// </summary>
	/// <remarks>To enable iCloud capabilities in the file picker on iOS, you'll need to add the iCloud Documents in your <c>Entitlements.plist</c>.</remarks>
	public interface IFilePicker
	{
		/// <summary>
		/// Opens the default file picker to allow the user to pick a single file.
		/// </summary>
		/// <param name="options">File picker options to use; may be <see langword="null"/> for default options.</param>
		/// <returns>File picking result object, or <see langword="null"/> if picking was cancelled by the user.</returns>
		/// <remarks>
		/// File types can be specified in order to limit files that can be selected, using a
		/// <see cref="PickOptions"/> object. Note that this method may re-throw platform specific exceptions that
		/// occurred during file picking. When calling <see cref="PickAsync(PickOptions?)"/> again while showing a file
		/// picker, the <see cref="Task"/> object that was returned from the first call is cancelled. Be sure to
		/// also handle the <see cref="TaskCanceledException"/> in this case.
		/// </remarks>
		Task<FileResult?> PickAsync(PickOptions? options = null);
 
		/// <summary>
		/// Opens the default file picker to allow the user to pick one or more files.
		/// </summary>
		/// <param name="options">File picker options to use; may be <see langword="null"/> for default options.</param>
		/// <returns>An IEnumerable of file picking result objects, or <see langword="null"/> if picking was cancelled by the user.</returns>
		/// <remarks>
		/// File types can be specified in order to limit files that can be selected, using a
		/// <see cref="PickOptions"/> object. Note that this method may re-throw platform specific exceptions that
		/// occurred during file picking. When calling <see cref="PickMultipleAsync(PickOptions?)"/> again while showing a file
		/// picker, the <see cref="Task"/> object that was returned from the first call is cancelled. Be sure to
		/// also handle the <see cref="TaskCanceledException"/> in this case.
		/// </remarks>
		Task<IEnumerable<FileResult>> PickMultipleAsync(PickOptions? options = null);
	}
 
	/// <summary>
	/// Lets the user pick a file from the device's storage.
	/// </summary>
	/// <remarks>To enable iCloud capabilities in the file picker on iOS, you'll need to add the iCloud Documents in your <c>Entitlements.plist</c>.</remarks>
	public static partial class FilePicker
	{
		/// <summary>
		/// Opens the default file picker to allow the user to pick a single file.
		/// </summary>
		/// <param name="options">File picker options to use; may be <see langword="null"/> for default options.</param>
		/// <returns>File picking result object, or <see langword="null"/> if picking was cancelled by the user.</returns>
		/// <remarks>
		/// File types can be specified in order to limit files that can be selected, using a
		/// <see cref="PickOptions"/> object. Note that this method may re-throw platform specific exceptions that
		/// occurred during file picking. When calling <see cref="PickAsync(PickOptions?)"/> again while showing a file
		/// picker, the <see cref="Task"/> object that was returned from the first call is cancelled. Be sure to
		/// also handle the <see cref="TaskCanceledException"/> in this case.
		/// </remarks>
		public static Task<FileResult?> PickAsync(PickOptions? options = null) =>
			Default.PickAsync(options);
 
		/// <summary>
		/// Opens the default file picker to allow the user to pick one or more files.
		/// </summary>
		/// <param name="options">File picker options to use; may be <see langword="null"/> for default options.</param>
		/// <returns>An IEnumerable of file picking result objects, or <see langword="null"/> if picking was cancelled by the user.</returns>
		/// <remarks>
		/// File types can be specified in order to limit files that can be selected, using a
		/// <see cref="PickOptions"/> object. Note that this method may re-throw platform specific exceptions that
		/// occurred during file picking. When calling <see cref="PickMultipleAsync(PickOptions?)"/> again while showing a file
		/// picker, the <see cref="Task"/> object that was returned from the first call is cancelled. Be sure to
		/// also handle the <see cref="TaskCanceledException"/> in this case.
		/// </remarks>
		public static Task<IEnumerable<FileResult>> PickMultipleAsync(PickOptions? options = null) =>
			Default.PickMultipleAsync(options);
 
		static IFilePicker? defaultImplementation;
 
		/// <summary>
		/// Provides the default implementation for static usage of this API.
		/// </summary>
		public static IFilePicker Default =>
			defaultImplementation ??= new FilePickerImplementation();
 
		internal static void SetDefault(IFilePicker? implementation) =>
			defaultImplementation = implementation;
	}
 
	partial class FilePickerImplementation : IFilePicker
	{
		public async Task<FileResult?> PickAsync(PickOptions? options = null) =>
			(await PlatformPickAsync(options))?.FirstOrDefault();
 
		public Task<IEnumerable<FileResult>> PickMultipleAsync(PickOptions? options = null) =>
			PlatformPickAsync(options ?? PickOptions.Default, true);
	}
 
	/// <summary>
	/// Represents the file types that are allowed to be picked by the user when using <see cref="IFilePicker"/>.
	/// </summary>
	public partial class FilePickerFileType
	{
		/// <summary>
		/// Predefined <see cref="FilePickerFileType"/> object for allowing picking image files only.
		/// </summary>
		public static readonly FilePickerFileType Images = PlatformImageFileType();
 
		/// <summary>
		/// Predefined <see cref="FilePickerFileType"/> object for allowing picking PNG image files only.
		/// </summary>
		public static readonly FilePickerFileType Png = PlatformPngFileType();
 
		/// <summary>
		/// Predefined <see cref="FilePickerFileType"/> object for allowing picking JPEG image files only.
		/// </summary>
		public static readonly FilePickerFileType Jpeg = PlatformJpegFileType();
 
		/// <summary>
		/// Predefined <see cref="FilePickerFileType"/> object for allowing picking video files only.
		/// </summary>
		public static readonly FilePickerFileType Videos = PlatformVideoFileType();
 
		/// <summary>
		/// Predefined <see cref="FilePickerFileType"/> object for allowing picking PDF files only.
		/// </summary>
		public static readonly FilePickerFileType Pdf = PlatformPdfFileType();
 
		readonly IDictionary<DevicePlatform, IEnumerable<string>> fileTypes;
 
		/// <summary>
		/// Initializes a new instance of the <see cref="FilePickerFileType"/> class.
		/// </summary>
		protected FilePickerFileType() =>
			fileTypes = new Dictionary<DevicePlatform, IEnumerable<string>>();
 
		/// <summary>
		/// Initializes a new instance of the <see cref="FilePickerFileType"/> class.
		/// </summary>
		/// <param name="fileTypes">
		/// A <see cref="IDictionary{TKey, TValue}"/> where the key is the platform
		/// and the value is a collection of file types that are allowed to be picked by the user.
		/// </param>
		/// <remarks>Note that the file types (specified in the <paramref name="fileTypes"/> value)
		/// should be identified differently per platform.</remarks>
		public FilePickerFileType(IDictionary<DevicePlatform, IEnumerable<string>> fileTypes) =>
			this.fileTypes = fileTypes;
 
		/// <summary>
		/// Gets the configured allowed file types that can be picked by the user for the current platform.
		/// </summary>
		/// <exception cref="PlatformNotSupportedException">Thrown if the current platform does not have any file types configured.</exception>
		public IEnumerable<string> Value => GetPlatformFileType(DeviceInfo.Current.Platform);
 
		/// <summary>
		/// Gets the configured allowed file types that can be picked by the user for the current platform.
		/// </summary>
		/// <exception cref="PlatformNotSupportedException">Thrown if the current platform does not have any file types configured.</exception>
		protected virtual IEnumerable<string> GetPlatformFileType(DevicePlatform platform)
		{
			if (fileTypes.TryGetValue(platform, out var type))
				return type;
 
			throw new PlatformNotSupportedException("This platform does not support this file type.");
		}
	}
 
	/// <summary>
	/// Represents file picking options that can be used to customize the working of <see cref="IFilePicker"/>.
	/// </summary>
	public class PickOptions
	{
		/// <summary>
		/// Default file picking options. This object can be used when no special pick options are necessary.
		/// </summary>
		public static PickOptions Default =>
			new PickOptions
			{
				FileTypes = null,
			};
 
		/// <summary>
		/// Predefined <see cref="PickOptions"/> object for picking image files only.
		/// </summary>
		public static PickOptions Images =>
			new PickOptions
			{
				FileTypes = FilePickerFileType.Images
			};
 
		/// <summary>
		/// Title used for the file picker that is shown to the user.
		/// </summary>
		/// <remarks>The file picker title is only used on the Android platform and only for certain file pickers. This title is not guaranteed to be shown.</remarks>
		public string? PickerTitle { get; set; }
 
		/// <summary>
		/// List of file types that file file picker should return.
		/// </summary>
		/// <remarks>
		/// On Android and iOS the files not matching this list are shown in the file picker,
		/// but will be grayed out and cannot be selected.
		/// When the <see cref="FilePickerFileType.Value"/> array is <see langword="null"/> or empty,
		/// all file types can be selected while picking.
		/// The contents of this array is platform specific; every platform has its own way to
		/// specify the file types.
		/// <para>On Android you can specify one or more MIME types, e.g.
		/// <c>image/png</c>. Additionally, wildcard characters can be used, e.g. <c>image/*</c></para>
		/// <para>On iOS, you can specify <c>UTType</c> constants, e.g. <c>UTType.Image</c>.</para>
		/// <para>On Windows, you can specify a list of extensions, like this: <c>".jpg", ".png"</c>.</para>
		///</remarks>
		public FilePickerFileType? FileTypes { get; set; }
	}
}