Skip to content
This repository was archived by the owner on May 15, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Samples/Samples.iOS/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>MinimumOSVersion</key>
<string>10.0</string>
<string>14.5</string>
<key>CFBundleDisplayName</key>
<string>Xamarin.Essentials</string>
<key>CFBundleIdentifier</key>
Expand Down
32 changes: 20 additions & 12 deletions Samples/Samples/View/MediaPickerPage.xaml
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
<views:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:Samples.View"
xmlns:viewmodels="clr-namespace:Samples.ViewModel"
x:Class="Samples.View.MediaPickerPage"
Title="Media Picker">
<views:BasePage
x:Class="Samples.View.MediaPickerPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:Samples.ViewModel"
xmlns:views="clr-namespace:Samples.View"
Title="Media Picker">
<views:BasePage.BindingContext>
<viewmodels:MediaPickerViewModel />
</views:BasePage.BindingContext>

<ScrollView>
<StackLayout Padding="10">
<Button Text="Pick photo" Command="{Binding PickPhotoCommand}" />
<Button Text="Capture photo" Command="{Binding CapturePhotoCommand}" />
<Button Text="Pick video" Command="{Binding PickVideoCommand}" />
<Button Text="Capture video" Command="{Binding CaptureVideoCommand}" />
<Button Command="{Binding PickPhotoCommand}" Text="Pick photo" />
<Button Command="{Binding PickPhotosCommand}" Text="Pick photos" />
<Button Command="{Binding CapturePhotoCommand}" Text="Capture photo" />
<Button Command="{Binding PickVideoCommand}" Text="Pick video" />
<Button Command="{Binding CaptureVideoCommand}" Text="Capture video" />

<Image VerticalOptions="FillAndExpand" Source="{Binding PhotoPath}" IsVisible="{Binding ShowPhoto}" />
<MediaElement VerticalOptions="FillAndExpand" Source="{Binding VideoPath}" IsVisible="{Binding ShowVideo}" />
<Image
IsVisible="{Binding ShowPhoto}"
Source="{Binding PhotoPath}"
VerticalOptions="FillAndExpand" />
<MediaElement
IsVisible="{Binding ShowVideo}"
Source="{Binding VideoPath}"
VerticalOptions="FillAndExpand" />
</StackLayout>
</ScrollView>

Expand Down
21 changes: 21 additions & 0 deletions Samples/Samples/ViewModel/MediaPickerViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Essentials;
Expand All @@ -18,6 +19,7 @@ public class MediaPickerViewModel : BaseViewModel
public MediaPickerViewModel()
{
PickPhotoCommand = new Command(DoPickPhoto);
PickPhotosCommand = new Command(DoPickPhotos);
CapturePhotoCommand = new Command(DoCapturePhoto, () => MediaPicker.IsCaptureSupported);

PickVideoCommand = new Command(DoPickVideo);
Expand All @@ -26,6 +28,8 @@ public MediaPickerViewModel()

public ICommand PickPhotoCommand { get; }

public ICommand PickPhotosCommand { get; }

public ICommand CapturePhotoCommand { get; }

public ICommand PickVideoCommand { get; }
Expand Down Expand Up @@ -72,6 +76,23 @@ async void DoPickPhoto()
}
}

async void DoPickPhotos()
{
try
{
var photos = await MediaPicker.PickPhotosAsync();
var photo = photos.FirstOrDefault();

await LoadPhotoAsync(photo);

Console.WriteLine($"PickPhotosAsync COMPLETED: {PhotoPath}");
}
catch (Exception ex)
{
Console.WriteLine($"PickPhotosAsync THREW: {ex.Message}");
}
}

async void DoCapturePhoto()
{
try
Expand Down
180 changes: 90 additions & 90 deletions Xamarin.Essentials.sln

Large diffs are not rendered by default.

18 changes: 17 additions & 1 deletion Xamarin.Essentials/FileSystem/FileSystem.ios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,27 @@ internal UIDocumentFileResult(NSUrl url)
internal override Task<Stream> PlatformOpenReadAsync()
{
Stream fileStream = File.OpenRead(FullPath);

return Task.FromResult(fileStream);
}
}

class NSUrlFileResult : FileResult
{
NSData data;

internal NSUrlFileResult(NSUrl url)
{
data = NSData.FromUrl(url);
FullPath = url.AbsoluteString;
FileName = Path.GetFileName(FullPath);
}

internal override Task<Stream> PlatformOpenReadAsync()
{
return Task.FromResult(data.AsStream());
}
}

class UIImageFileResult : FileResult
{
readonly UIImage uiImage;
Expand Down
7 changes: 7 additions & 0 deletions Xamarin.Essentials/MediaPicker/MediaPicker.android.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Android.Content;
using Android.Content.PM;
Expand All @@ -15,6 +16,9 @@ static bool PlatformIsCaptureSupported
static Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options)
=> PlatformPickAsync(options, true);

static Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions = null)
=> PlatformPicksAsync(options, pickerOptions);

static Task<FileResult> PlatformPickVideoAsync(MediaPickerOptions options)
=> PlatformPickAsync(options, false);

Expand Down Expand Up @@ -105,5 +109,8 @@ void OnCreate(Intent intent)
return null;
}
}

static async Task<IEnumerable<FileResult>> PlatformPicksAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions)
=> await Task.FromResult(new List<FileResult>());
}
}
83 changes: 80 additions & 3 deletions Xamarin.Essentials/MediaPicker/MediaPicker.ios.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Foundation;
using MobileCoreServices;
using Photos;
using PhotosUI;
using UIKit;

namespace Xamarin.Essentials
Expand All @@ -18,6 +21,9 @@ static bool PlatformIsCaptureSupported
static Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options)
=> PhotoAsync(options, true, true);

static Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions)
=> PhotosAsync(options, pickerOptions);

static Task<FileResult> PlatformCapturePhotoAsync(MediaPickerOptions options)
=> PhotoAsync(options, true, false);

Expand All @@ -27,7 +33,7 @@ static Task<FileResult> PlatformPickVideoAsync(MediaPickerOptions options)
static Task<FileResult> PlatformCaptureVideoAsync(MediaPickerOptions options)
=> PhotoAsync(options, false, false);

static async Task<FileResult> PhotoAsync(MediaPickerOptions options, bool photo, bool pickExisting)
static async Task<FileResult> PhotoAsync(MediaPickerOptions options, bool photo, bool pickExisting, bool multi = false)
{
var sourceType = pickExisting ? UIImagePickerControllerSourceType.PhotoLibrary : UIImagePickerControllerSourceType.Camera;
var mediaType = photo ? UTType.Image : UTType.Movie;
Expand All @@ -53,6 +59,7 @@ static async Task<FileResult> PhotoAsync(MediaPickerOptions options, bool photo,
picker.SourceType = sourceType;
picker.MediaTypes = new string[] { mediaType };
picker.AllowsEditing = false;

if (!photo && !pickExisting)
picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Video;

Expand Down Expand Up @@ -92,15 +99,15 @@ static void GetFileResult(NSDictionary info, TaskCompletionSource<FileResult> tc
{
try
{
tcs.TrySetResult(DictionaryToMediaFile(info));
tcs.TrySetResult(DictionaryToFileResult(info));
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}

static FileResult DictionaryToMediaFile(NSDictionary info)
static FileResult DictionaryToFileResult(NSDictionary info)
{
if (info == null)
return null;
Expand Down Expand Up @@ -172,5 +179,75 @@ class PhotoPickerPresentationControllerDelegate : UIAdaptivePresentationControll
public override void DidDismiss(UIPresentationController presentationController) =>
CompletedHandler?.Invoke(null);
}

static async Task<IEnumerable<FileResult>> PhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions)
{
var config = new PHPickerConfiguration
{
SelectionLimit = 3,
Filter = PHPickerFilter.ImagesFilter
};

var picker = new PHPickerViewController(config);

if (!string.IsNullOrWhiteSpace(options?.Title))
picker.Title = options.Title;

var tcs = new TaskCompletionSource<IEnumerable<FileResult>>(picker);

picker.Delegate = new PhotosPickerDelegate()
{
CompletedHandler = results => GetFileResults(results, tcs)
};

var vc = Platform.GetCurrentViewController(true);

await vc.PresentViewControllerAsync(picker, true);

var result = await tcs.Task;

await vc.DismissViewControllerAsync(true);

picker?.Dispose();
picker = null;

return result;
}

static void GetFileResults(PHPickerResult[] results, TaskCompletionSource<IEnumerable<FileResult>> tcs)
{
try
{
var fileResults = new List<FileResult>();
foreach (var result in results)
{
foreach (var registeredItemType in result.ItemProvider.RegisteredTypeIdentifiers)
{
result.ItemProvider.LoadFileRepresentation(registeredItemType, (url, error) =>
{
if (error != null || url == null)
return;

FileResult fileResult = new NSUrlFileResult(url);
var taskCompletionSource = new TaskCompletionSource<FileResult>();
taskCompletionSource.SetResult(fileResult);
fileResults.Add(taskCompletionSource.Task.Result);
});
}
}
tcs.TrySetResult(fileResults);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}

class PhotosPickerDelegate : PHPickerViewControllerDelegate
{
public Action<PHPickerResult[]> CompletedHandler { get; set; }

public override void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results) => CompletedHandler?.Invoke(results);
}
}
}
5 changes: 4 additions & 1 deletion Xamarin.Essentials/MediaPicker/MediaPicker.macos.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ static bool PlatformIsCaptureSupported
static async Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options)
=> new FileResult(await FilePicker.PickAsync(new PickOptions
{
FileTypes = FilePickerFileType.Images
FileTypes = FilePickerFileType.Images
}));

static async Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions = null)
=> await Task.FromResult(new List<FileResult>());

static Task<FileResult> PlatformCapturePhotoAsync(MediaPickerOptions options)
=> PlatformPickPhotoAsync(options);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Xamarin.Essentials
Expand All @@ -12,6 +11,9 @@ public static partial class MediaPicker
static Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options) =>
throw new NotImplementedInReferenceAssemblyException();

static Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions) =>
throw new NotImplementedInReferenceAssemblyException();

static Task<FileResult> PlatformCapturePhotoAsync(MediaPickerOptions options) =>
throw new NotImplementedInReferenceAssemblyException();

Expand Down
10 changes: 8 additions & 2 deletions Xamarin.Essentials/MediaPicker/MediaPicker.shared.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace Xamarin.Essentials
Expand All @@ -12,6 +11,9 @@ public static bool IsCaptureSupported
public static Task<FileResult> PickPhotoAsync(MediaPickerOptions options = null) =>
PlatformPickPhotoAsync(options);

public static Task<IEnumerable<FileResult>> PickPhotosAsync(MediaPickerOptions options = null, MultiPickerOptions pickerOptions = null) =>
PlatformPickPhotosAsync(options, pickerOptions);

public static Task<FileResult> CapturePhotoAsync(MediaPickerOptions options = null)
{
if (!IsCaptureSupported)
Expand All @@ -36,4 +38,8 @@ public class MediaPickerOptions
{
public string Title { get; set; }
}

public class MultiPickerOptions
{
}
}
3 changes: 3 additions & 0 deletions Xamarin.Essentials/MediaPicker/MediaPicker.tizen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ static async Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options)
FileTypes = FilePickerFileType.Images
});

static async Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions = null)
=> await Task.FromResult(new List<FileResult>());

static Task<FileResult> PlatformCapturePhotoAsync(MediaPickerOptions options)
=> PlatformMediaAsync(options, true);

Expand Down
4 changes: 4 additions & 0 deletions Xamarin.Essentials/MediaPicker/MediaPicker.uwp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Windows.Media.Capture;
using Windows.Storage.Pickers;
using System.Collections.Generic;

namespace Xamarin.Essentials
{
Expand All @@ -14,6 +15,9 @@ static bool PlatformIsCaptureSupported
static Task<FileResult> PlatformPickPhotoAsync(MediaPickerOptions options)
=> PickAsync(options, true);

static async Task<IEnumerable<FileResult>> PlatformPickPhotosAsync(MediaPickerOptions options, MultiPickerOptions pickerOptions = null)
=> await Task.FromResult(new List<FileResult>());

static Task<FileResult> PlatformPickVideoAsync(MediaPickerOptions options)
=> PickAsync(options, false);

Expand Down