Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Commit

Permalink
Merge pull request #644 from patchandthat/cancellation-support
Browse files Browse the repository at this point in the history
Cancellation support
  • Loading branch information
jamesmontemagno authored Dec 21, 2018
2 parents 542512b + dad9c64 commit d2a6711
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 67 deletions.
55 changes: 36 additions & 19 deletions src/Media.Plugin/Android/MediaImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Plugin.CurrentActivity;
using System.Collections.Generic;
using System.Linq;
using Android.App;
using Permission = Plugin.Permissions.Abstractions.Permission;

namespace Plugin.Media
Expand All @@ -27,10 +28,12 @@ public class MediaImplementation : IMedia
const string TAG_PIXEL_X_DIMENSION = "PixelXDimension";
const string TAG_PIXEL_Y_DIMENSION = "PixelYDimension";

internal static event EventHandler CancelRequested;

/// <summary>
/// Implementation
/// </summary>
public MediaImplementation()
/// Implementation
/// </summary>
public MediaImplementation()
{

this.context = Android.App.Application.Context;
Expand Down Expand Up @@ -82,13 +85,13 @@ bool IsValidExif(ExifInterface exif)
/// Picks a photo from the default gallery
/// </summary>
/// <returns>Media file or null if canceled</returns>
public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken))
{
if (!(await RequestStoragePermission()))
{
throw new MediaPermissionException(Permission.Storage);
}
var media = await TakeMediaAsync("image/*", Intent.ActionPick, null);
var media = await TakeMediaAsync("image/*", Intent.ActionPick, null, token);

if (options == null)
options = new PickMediaOptions();
Expand Down Expand Up @@ -132,12 +135,13 @@ public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
}


/// <summary>
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <returns>Media file of photo or null if canceled</returns>
public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
/// <summary>
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <param name="token">Cancellation token</param>
/// <returns>Media file of photo or null if canceled</returns>
public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken))
{
if (!IsCameraAvailable)
throw new NotSupportedException();
Expand All @@ -150,7 +154,7 @@ public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)

VerifyOptions(options);

var media = await TakeMediaAsync("image/*", MediaStore.ActionImageCapture, options);
var media = await TakeMediaAsync("image/*", MediaStore.ActionImageCapture, options, token);

if (string.IsNullOrWhiteSpace(media?.Path))
return media;
Expand Down Expand Up @@ -245,23 +249,23 @@ public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
/// Picks a video from the default gallery
/// </summary>
/// <returns>Media file of video or null if canceled</returns>
public async Task<MediaFile> PickVideoAsync()
public async Task<MediaFile> PickVideoAsync(CancellationToken token = default(CancellationToken))
{

if (!(await RequestStoragePermission()))
{
throw new MediaPermissionException(Permission.Storage);
}

return await TakeMediaAsync("video/*", Intent.ActionPick, null);
return await TakeMediaAsync("video/*", Intent.ActionPick, null, token);
}

/// <summary>
/// Take a video with specified options
/// </summary>
/// <param name="options">Video Media Options</param>
/// <returns>Media file of new video or null if canceled</returns>
public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)
public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken))
{
if (!IsCameraAvailable)
throw new NotSupportedException();
Expand All @@ -273,7 +277,7 @@ public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)

VerifyOptions(options);

return await TakeMediaAsync("video/*", MediaStore.ActionVideoCapture, options);
return await TakeMediaAsync("video/*", MediaStore.ActionVideoCapture, options, token);
}

private readonly Context context;
Expand Down Expand Up @@ -459,16 +463,18 @@ private int GetRequestId()
return id;
}

private Task<MediaFile> TakeMediaAsync(string type, string action, StoreMediaOptions options)
private Task<MediaFile> TakeMediaAsync(string type, string action, StoreMediaOptions options, CancellationToken token = default(CancellationToken))
{
int id = GetRequestId();

if (token.IsCancellationRequested) return Task.FromResult((MediaFile) null);

var ntcs = new TaskCompletionSource<MediaFile>(id);
if (Interlocked.CompareExchange(ref completionSource, ntcs, null) != null)
throw new InvalidOperationException("Only one operation can be active at a time");

context.StartActivity(CreateMediaIntent(id, type, action, options));

EventHandler<MediaPickedEventArgs> handler = null;
handler = (s, e) =>
{
Expand All @@ -487,7 +493,18 @@ private Task<MediaFile> TakeMediaAsync(string type, string action, StoreMediaOpt
tcs.SetResult(e.Media);
};

MediaPickerActivity.MediaPicked += handler;
token.Register(() =>
{
var tcs = Interlocked.Exchange(ref this.completionSource, null);

MediaPickerActivity.MediaPicked -= handler;
CancelRequested?.Invoke(null, EventArgs.Empty);
CancelRequested = null;

tcs.SetResult(null);
});

MediaPickerActivity.MediaPicked += handler;

return completionSource.Task;
}
Expand Down
16 changes: 12 additions & 4 deletions src/Media.Plugin/Android/MediaPickerActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,9 @@ protected override void OnSaveInstanceState(Bundle outState)
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
MediaImplementation.CancelRequested += CancellationRequested;

var b = (savedInstanceState ?? Intent.Extras);
var b = (savedInstanceState ?? Intent.Extras);

var ran = b.GetBoolean("ran", defaultValue: false);

Expand Down Expand Up @@ -218,7 +219,14 @@ protected override void OnCreate(Bundle savedInstanceState)
}
}

private void Touch()
private void CancellationRequested(object sender, EventArgs e)
{
FinishActivity(id);
DeleteOutputFile();
Finish();
}

private void Touch()
{
if (path.Scheme != "file")
return;
Expand Down Expand Up @@ -332,9 +340,9 @@ internal static Task<MediaPickedEventArgs> GetMediaFileAsync(Context context, in
protected override async void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
completed = true;
MediaImplementation.CancelRequested -= CancellationRequested;
base.OnActivityResult(requestCode, resultCode, data);




if (tasked)
{
Expand Down
1 change: 0 additions & 1 deletion src/Media.Plugin/Media.Plugin.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
<Project Sdk="MSBuild.Sdk.Extras/1.6.65">

<PropertyGroup>
<TargetFrameworks>netstandard1.0;netstandard2.0;MonoAndroid71;MonoAndroid80;MonoAndroid81;Xamarin.iOS10;uap10.0.16299;Tizen40</TargetFrameworks>
<AssemblyName>Plugin.Media</AssemblyName>
Expand Down
51 changes: 28 additions & 23 deletions src/Media.Plugin/Shared/IMedia.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Plugin.Media.Abstractions
Expand Down Expand Up @@ -36,31 +37,35 @@ public interface IMedia
/// </summary>
bool IsPickVideoSupported { get; }

/// <summary>
/// Picks a photo from the default gallery
/// </summary>
/// <returns>Media file or null if canceled</returns>
Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null);
/// <summary>
/// Picks a photo from the default gallery
/// </summary>
/// <param name="token">Cancellation token</param>
/// <returns>Media file or null if canceled</returns>
Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken));

/// <summary>
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <returns>Media file of photo or null if canceled</returns>
Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options);
/// <summary>
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <param name="token">Cancellation token</param>
/// <returns>Media file of photo or null if canceled</returns>
Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken));

/// <summary>
/// Picks a video from the default gallery
/// </summary>
/// <returns>Media file of video or null if canceled</returns>
Task<MediaFile> PickVideoAsync();
/// <summary>
/// Picks a video from the default gallery
/// </summary>
/// <param name="token">Cancellation token</param>
/// <returns>Media file of video or null if canceled</returns>
Task<MediaFile> PickVideoAsync(CancellationToken token = default(CancellationToken));

/// <summary>
/// Take a video with specified options
/// </summary>
/// <param name="options">Video Media Options</param>
/// <returns>Media file of new video or null if canceled</returns>
Task<MediaFile> TakeVideoAsync(StoreVideoOptions options);
/// <summary>
/// Take a video with specified options
/// </summary>
/// <param name="options">Video Media Options</param>
/// <param name="token">Cancellation token</param>
/// <returns>Media file of new video or null if canceled</returns>
Task<MediaFile> TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken));

}
}
}
12 changes: 8 additions & 4 deletions src/Media.Plugin/Tizen/MediaImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ public MediaImplementation()
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of photo or null if canceled</returns>
public Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
public Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken))
{
if (!IsCameraAvailable || !IsTakePhotoSupported)
{
Expand All @@ -96,8 +97,9 @@ public Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
/// Take a video with specified options
/// </summary>
/// <param name="options">Video Media Options</param>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of new video or null if canceled</returns>
public Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)
public Task<MediaFile> TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken))
{
if (!IsCameraAvailable || !IsTakeVideoSupported) {
Log.Error(LOG_TAG, "TakeVideo is not supported");
Expand All @@ -117,8 +119,9 @@ public Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)
/// <summary>
/// Picks a video from the default gallery
/// </summary>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of video or null if canceled</returns>
public Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
public Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken))
{
if (!IsPickPhotoSupported)
{
Expand All @@ -139,8 +142,9 @@ public Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
/// <summary>
/// Picks a video from the default gallery
/// </summary>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of video or null if canceled</returns>
public Task<MediaFile> PickVideoAsync()
public Task<MediaFile> PickVideoAsync(CancellationToken token = default(CancellationToken))
{
if (!IsPickVideoSupported)
{
Expand Down
12 changes: 8 additions & 4 deletions src/Media.Plugin/UWP/MediaImplementation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ public bool IsCameraAvailable
/// Take a photo async with specified options
/// </summary>
/// <param name="options">Camera Media Options</param>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of photo or null if canceled</returns>
public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options)
public async Task<MediaFile> TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken))
{
if (!initialized)
await Initialize();
Expand Down Expand Up @@ -182,8 +183,9 @@ CameraCaptureUIMaxPhotoResolution GetMaxResolution(PhotoSize photoSize, int cust
/// <summary>
/// Picks a photo from the default gallery
/// </summary>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file or null if canceled</returns>
public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken))
{
var picker = new FileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
Expand Down Expand Up @@ -226,8 +228,9 @@ public async Task<MediaFile> PickPhotoAsync(PickMediaOptions options = null)
/// Take a video with specified options
/// </summary>
/// <param name="options">Video Media Options</param>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of new video or null if canceled</returns>
public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)
public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken))
{
if (!initialized)
await Initialize();
Expand Down Expand Up @@ -271,8 +274,9 @@ public async Task<MediaFile> TakeVideoAsync(StoreVideoOptions options)
/// <summary>
/// Picks a video from the default gallery
/// </summary>
/// <param name="token">Cancellation token (currently ignored)</param>
/// <returns>Media file of video or null if canceled</returns>
public async Task<MediaFile> PickVideoAsync()
public async Task<MediaFile> PickVideoAsync(CancellationToken token = default(CancellationToken))
{
var picker = new FileOpenPicker()
{
Expand Down
Loading

0 comments on commit d2a6711

Please sign in to comment.