diff --git a/src/Media.Plugin/iOS/ECLImagePickerViewController.cs b/src/Media.Plugin/iOS/ECLImagePickerViewController.cs index 6f365c50..cd2e6761 100644 --- a/src/Media.Plugin/iOS/ECLImagePickerViewController.cs +++ b/src/Media.Plugin/iOS/ECLImagePickerViewController.cs @@ -177,7 +177,7 @@ private MediaFile GetPictureMediaFile(ALAsset asset, long index = 0) phOptions.ProgressHandler = (double progress, NSError error, out bool stop, NSDictionary info) => { Debug.WriteLine($"Progress: {progress.ToString()}"); - + stop = false; }; @@ -190,7 +190,7 @@ private MediaFile GetPictureMediaFile(ALAsset asset, long index = 0) }); } else - { + { manager.RequestImageData(ph, phOptions, (data, i, orientation, k) => { if (data != null) @@ -206,25 +206,33 @@ private MediaFile GetPictureMediaFile(ALAsset asset, long index = 0) image = new UIImage(cgImage, 1.0f, (UIImageOrientation)rep.Orientation); } - var path = MediaPickerDelegate.GetOutputPath(MediaImplementation.TypeImage, + string path = MediaPickerDelegate.GetOutputPath(MediaImplementation.TypeImage, options.Directory ?? "temp", options.Name, asset.AssetUrl?.PathExtension, index); + bool isPng = Path.GetExtension(path).ToLowerInvariant() == ".png"; cgImage?.Dispose(); cgImage = null; rep?.Dispose(); rep = null; - //There might be cases when the original image cannot be retrieved while image thumb was still present. - //Then no need to try to save it as we will get an exception here - //TODO: Ideally, we should notify the client that we failed to get original image - //TODO: Otherwise, it might be confusing to the user, that he saw the thumb, but did not get the image - if (image == null) - { - return null; - } - - image.AsJPEG().Save(path, true); + //There might be cases when the original image cannot be retrieved while image thumb was still present. + //Then no need to try to save it as we will get an exception here + //TODO: Ideally, we should notify the client that we failed to get original image + //TODO: Otherwise, it might be confusing to the user, that he saw the thumb, but did not get the image + if (image == null) + { + return null; + } + + if (isPng) + { + image.AsPNG().Save(path, true); + } + else + { + image.AsJPEG().Save(path, true); + } image?.Dispose(); image = null; @@ -340,22 +348,22 @@ void GroupsEnumerator(ALAssetsGroup agroup, ref bool stop) return; } - //We show photos only. Let's get only them + //We show photos only. Let's get only them agroup.SetAssetsFilter(ALAssetsFilter.AllPhotos); - //do not add empty album - if (agroup.Count == 0) - { - return; - } + //do not add empty album + if (agroup.Count == 0) + { + return; + } + + //ALAssetsGroupType.All might have duplicated albums. let's skip the album if we already have it + if (assetGroups.Any(g => g.PersistentID == agroup.PersistentID)) + { + return; + } - //ALAssetsGroupType.All might have duplicated albums. let's skip the album if we already have it - if (assetGroups.Any(g => g.PersistentID == agroup.PersistentID)) - { - return; - } - - assetGroups.Add(agroup); + assetGroups.Add(agroup); dispatcher.BeginInvokeOnMainThread(ReloadTableView); } @@ -383,7 +391,7 @@ public override UITableViewCell GetCell(UITableView tableView, NSIndexPath index // Get count var g = assetGroups[indexPath.Row]; - + var gCount = g.Count; cell.TextLabel.Text = string.Format("{0} ({1})", g.Name, gCount); try @@ -403,7 +411,7 @@ public override void RowSelected(UITableView tableView, NSIndexPath indexPath) { var assetGroup = assetGroups[indexPath.Row]; var picker = new ELCAssetTablePicker(assetGroup); - + picker.LoadingTitle = LoadingTitle; picker.PickAssetTitle = PickAssetTitle; picker.DoneButtonTitle = DoneButtonTitle; @@ -470,12 +478,14 @@ public ELCImagePickerViewController Parent set => parent = new WeakReference(value); } - public ELCAssetTablePicker(ALAssetsGroup assetGroup) : base(new UICollectionViewFlowLayout { + public ELCAssetTablePicker(ALAssetsGroup assetGroup) : base(new UICollectionViewFlowLayout + { ItemSize = new CGSize(75, 75), MinimumLineSpacing = 4, MinimumInteritemSpacing = 4, SectionInset = new UIEdgeInsets(0, 4, 0, 4), - ScrollDirection = UICollectionViewScrollDirection.Vertical }) + ScrollDirection = UICollectionViewScrollDirection.Vertical + }) { this.assetGroup = assetGroup; } @@ -560,7 +570,7 @@ private void AssetSelected(NSIndexPath targetIndexPath, bool selected) asset = null; if (mediaFile != null) { - Parent?.SelectedMediaFiles(new List{ mediaFile }); + Parent?.SelectedMediaFiles(new List { mediaFile }); } else { @@ -614,10 +624,10 @@ private async void DoneClicked(object sender = null, EventArgs e = null) var selectedMediaFiles = new MediaFile[selectedItemsCount]; //Create activity indicator if we have selected items. - //It will give the user some visual feedback that the app is still working - //if the media have to be downloaded from the iCloud - UIView pageOverlay = null; - UIActivityIndicatorView activityIndicator = null; + //It will give the user some visual feedback that the app is still working + //if the media have to be downloaded from the iCloud + UIView pageOverlay = null; + UIActivityIndicatorView activityIndicator = null; if (selectedItemsCount > 0) { InvokeOnMainThread(() => @@ -654,10 +664,10 @@ private async void DoneClicked(object sender = null, EventArgs e = null) await Task.WhenAll(tasks); - pageOverlay?.RemoveFromSuperview(); - activityIndicator?.RemoveFromSuperview(); + pageOverlay?.RemoveFromSuperview(); + activityIndicator?.RemoveFromSuperview(); - //Some items in the array might be null. Let's remove them. + //Some items in the array might be null. Let's remove them. parent?.SelectedMediaFiles(selectedMediaFiles.Where(mf => mf != null).ToList()); } @@ -682,7 +692,8 @@ public ALAsset Asset public override bool Highlighted { get => base.Highlighted; - set { + set + { HighlightedView.Hidden = !value; base.Highlighted = value; } diff --git a/src/Media.Plugin/iOS/MediaImplementation.cs b/src/Media.Plugin/iOS/MediaImplementation.cs index a5d09a4d..a7e64f3d 100644 --- a/src/Media.Plugin/iOS/MediaImplementation.cs +++ b/src/Media.Plugin/iOS/MediaImplementation.cs @@ -14,64 +14,64 @@ namespace Plugin.Media { - /// - /// Implementation for Media - /// - public class MediaImplementation : IMedia - { - /// - /// Color of the status bar - /// - public static UIStatusBarStyle StatusBarStyle { get; set; } + /// + /// Implementation for Media + /// + public class MediaImplementation : IMedia + { + /// + /// Color of the status bar + /// + public static UIStatusBarStyle StatusBarStyle { get; set; } /// public Task Initialize() => Task.FromResult(true); - /// - /// Implementation - /// - public MediaImplementation() - { - StatusBarStyle = UIApplication.SharedApplication.StatusBarStyle; - IsCameraAvailable = UIImagePickerController.IsCameraDeviceAvailable(UIKit.UIImagePickerControllerCameraDevice.Front) + /// + /// Implementation + /// + public MediaImplementation() + { + StatusBarStyle = UIApplication.SharedApplication.StatusBarStyle; + IsCameraAvailable = UIImagePickerController.IsCameraDeviceAvailable(UIKit.UIImagePickerControllerCameraDevice.Front) | UIImagePickerController.IsCameraDeviceAvailable(UIKit.UIImagePickerControllerCameraDevice.Rear); - var availableCameraMedia = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.Camera) ?? new string[0]; - var avaialbleLibraryMedia = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary) ?? new string[0]; + var availableCameraMedia = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.Camera) ?? new string[0]; + var avaialbleLibraryMedia = UIImagePickerController.AvailableMediaTypes(UIImagePickerControllerSourceType.PhotoLibrary) ?? new string[0]; + + foreach (var type in availableCameraMedia.Concat(avaialbleLibraryMedia)) + { + if (type == TypeMovie) + IsTakeVideoSupported = IsPickVideoSupported = true; + else if (type == TypeImage) + IsTakePhotoSupported = IsPickPhotoSupported = true; + } + } + /// + public bool IsCameraAvailable { get; } - foreach (var type in availableCameraMedia.Concat(avaialbleLibraryMedia)) - { - if (type == TypeMovie) - IsTakeVideoSupported = IsPickVideoSupported = true; - else if (type == TypeImage) - IsTakePhotoSupported = IsPickPhotoSupported = true; - } - } - /// - public bool IsCameraAvailable { get; } + /// + public bool IsTakePhotoSupported { get; } - /// - public bool IsTakePhotoSupported { get; } + /// + public bool IsPickPhotoSupported { get; } - /// - public bool IsPickPhotoSupported { get; } + /// + public bool IsTakeVideoSupported { get; } - /// - public bool IsTakeVideoSupported { get; } + /// + public bool IsPickVideoSupported { get; } - /// - public bool IsPickVideoSupported { get; } - - /// - /// Picks a photo from the default gallery - /// - /// Media file or null if canceled - public async Task PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken)) - { - if (!IsPickPhotoSupported) - throw new NotSupportedException(); + /// + /// Picks a photo from the default gallery + /// + /// Media file or null if canceled + public async Task PickPhotoAsync(PickMediaOptions options = null, CancellationToken token = default(CancellationToken)) + { + if (!IsPickPhotoSupported) + throw new NotSupportedException(); //Does not need permission on iOS 11 @@ -93,10 +93,10 @@ public MediaImplementation() SaveMetaData = options?.SaveMetaData ?? true, SaveToAlbum = false, ModalPresentationStyle = options?.ModalPresentationStyle ?? MediaPickerModalPresentationStyle.FullScreen, - }; + }; - return await GetMediaAsync(UIImagePickerControllerSourceType.PhotoLibrary, TypeImage, cameraOptions, token); - } + return await GetMediaAsync(UIImagePickerControllerSourceType.PhotoLibrary, TypeImage, cameraOptions, token); + } public async Task> PickPhotosAsync(PickMediaOptions options = null, MultiPickerOptions pickerOptions = null, CancellationToken token = default(CancellationToken)) { @@ -127,23 +127,23 @@ public MediaImplementation() return await GetMediasAsync(UIImagePickerControllerSourceType.PhotoLibrary, TypeImage, cameraOptions, pickerOptions, token); } - /// - /// Take a photo async with specified options - /// - /// Camera Media Options - /// Media file of photo or null if canceled - public async Task TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken)) - { - if (!IsTakePhotoSupported) - throw new NotSupportedException(); - if (!IsCameraAvailable) - throw new NotSupportedException(); - - CheckUsageDescription(cameraDescription); + /// + /// Take a photo async with specified options + /// + /// Camera Media Options + /// Media file of photo or null if canceled + public async Task TakePhotoAsync(StoreCameraMediaOptions options, CancellationToken token = default(CancellationToken)) + { + if (!IsTakePhotoSupported) + throw new NotSupportedException(); + if (!IsCameraAvailable) + throw new NotSupportedException(); + + CheckUsageDescription(cameraDescription); if (options.SaveToAlbum) CheckUsageDescription(photoAddDescription); - VerifyCameraOptions(options); + VerifyCameraOptions(options); var permissionsToCheck = new List { Permission.Camera }; if (options.SaveToAlbum) @@ -151,22 +151,22 @@ public MediaImplementation() await CheckPermissions(permissionsToCheck.ToArray()); - return await GetMediaAsync(UIImagePickerControllerSourceType.Camera, TypeImage, options, token); - } + return await GetMediaAsync(UIImagePickerControllerSourceType.Camera, TypeImage, options, token); + } + + /// + /// Picks a video from the default gallery + /// + /// Media file of video or null if canceled + public async Task PickVideoAsync(CancellationToken token = default(CancellationToken)) + { + if (!IsPickVideoSupported) + throw new NotSupportedException(); - /// - /// Picks a video from the default gallery - /// - /// Media file of video or null if canceled - public async Task PickVideoAsync(CancellationToken token = default(CancellationToken)) - { - if (!IsPickVideoSupported) - throw new NotSupportedException(); + var backgroundTask = UIApplication.SharedApplication.BeginBackgroundTask(() => { }); - var backgroundTask = UIApplication.SharedApplication.BeginBackgroundTask(() => { }); - //Not needed on iOS 11 since it runs in different process if (!UIDevice.CurrentDevice.CheckSystemVersion(11, 0)) { @@ -176,25 +176,25 @@ public MediaImplementation() var media = await GetMediaAsync(UIImagePickerControllerSourceType.PhotoLibrary, TypeMovie, token: token); - UIApplication.SharedApplication.EndBackgroundTask(backgroundTask); + UIApplication.SharedApplication.EndBackgroundTask(backgroundTask); - return media; - } - + return media; + } - /// - /// Take a video with specified options - /// - /// Video Media Options - /// Media file of new video or null if canceled - public async Task TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken)) - { - if (!IsTakeVideoSupported) - throw new NotSupportedException(); - if (!IsCameraAvailable) - throw new NotSupportedException(); - CheckUsageDescription(cameraDescription, microphoneDescription); + /// + /// Take a video with specified options + /// + /// Video Media Options + /// Media file of new video or null if canceled + public async Task TakeVideoAsync(StoreVideoOptions options, CancellationToken token = default(CancellationToken)) + { + if (!IsTakeVideoSupported) + throw new NotSupportedException(); + if (!IsCameraAvailable) + throw new NotSupportedException(); + + CheckUsageDescription(cameraDescription, microphoneDescription); if (options.SaveToAlbum) CheckUsageDescription(photoAddDescription); @@ -207,91 +207,91 @@ public MediaImplementation() await CheckPermissions(permissionsToCheck.ToArray()); - return await GetMediaAsync(UIImagePickerControllerSourceType.Camera, TypeMovie, options, token); - } + return await GetMediaAsync(UIImagePickerControllerSourceType.Camera, TypeMovie, options, token); + } - private UIPopoverController popover; + private UIPopoverController popover; private UIImagePickerControllerDelegate pickerDelegate; - /// - /// image type - /// - public const string TypeImage = "public.image"; - /// - /// movie type - /// - public const string TypeMovie = "public.movie"; - - private void VerifyOptions(StoreMediaOptions options) - { - if (options == null) - throw new ArgumentNullException("options"); - if (options.Directory != null && Path.IsPathRooted(options.Directory)) - throw new ArgumentException("options.Directory must be a relative path", "options"); - } - - private void VerifyCameraOptions(StoreCameraMediaOptions options) - { - VerifyOptions(options); - if (!Enum.IsDefined(typeof(CameraDevice), options.DefaultCamera)) - throw new ArgumentException("options.Camera is not a member of CameraDevice"); - } - - private static MediaPickerController SetupController(MediaPickerDelegate mpDelegate, UIImagePickerControllerSourceType sourceType, string mediaType, StoreCameraMediaOptions options = null) - { - var picker = new MediaPickerController(mpDelegate); - picker.MediaTypes = new[] { mediaType }; - picker.SourceType = sourceType; - - if (sourceType == UIImagePickerControllerSourceType.Camera) - { - picker.CameraDevice = GetUICameraDevice(options.DefaultCamera); - picker.AllowsEditing = options?.AllowCropping ?? false; - - if (options.OverlayViewProvider != null) - { - var overlay = options.OverlayViewProvider(); - if (overlay is UIView) - { - picker.CameraOverlayView = overlay as UIView; - } - } - if (mediaType == TypeImage) - { - picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Photo; - } - else if (mediaType == TypeMovie) - { - var voptions = (StoreVideoOptions)options; - - picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Video; - picker.VideoQuality = GetQuailty(voptions.Quality); - picker.VideoMaximumDuration = voptions.DesiredLength.TotalSeconds; - } - } - - return picker; - } - - private Task GetMediaAsync(UIImagePickerControllerSourceType sourceType, string mediaType, StoreCameraMediaOptions options = null, CancellationToken token = default(CancellationToken)) - { - + /// + /// image type + /// + public const string TypeImage = "public.image"; + /// + /// movie type + /// + public const string TypeMovie = "public.movie"; + + private void VerifyOptions(StoreMediaOptions options) + { + if (options == null) + throw new ArgumentNullException("options"); + if (options.Directory != null && Path.IsPathRooted(options.Directory)) + throw new ArgumentException("options.Directory must be a relative path", "options"); + } + + private void VerifyCameraOptions(StoreCameraMediaOptions options) + { + VerifyOptions(options); + if (!Enum.IsDefined(typeof(CameraDevice), options.DefaultCamera)) + throw new ArgumentException("options.Camera is not a member of CameraDevice"); + } + + private static MediaPickerController SetupController(MediaPickerDelegate mpDelegate, UIImagePickerControllerSourceType sourceType, string mediaType, StoreCameraMediaOptions options = null) + { + var picker = new MediaPickerController(mpDelegate); + picker.MediaTypes = new[] { mediaType }; + picker.SourceType = sourceType; + + if (sourceType == UIImagePickerControllerSourceType.Camera) + { + picker.CameraDevice = GetUICameraDevice(options.DefaultCamera); + picker.AllowsEditing = options?.AllowCropping ?? false; + + if (options.OverlayViewProvider != null) + { + var overlay = options.OverlayViewProvider(); + if (overlay is UIView) + { + picker.CameraOverlayView = overlay as UIView; + } + } + if (mediaType == TypeImage) + { + picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Photo; + } + else if (mediaType == TypeMovie) + { + var voptions = (StoreVideoOptions)options; + + picker.CameraCaptureMode = UIImagePickerControllerCameraCaptureMode.Video; + picker.VideoQuality = GetQuailty(voptions.Quality); + picker.VideoMaximumDuration = voptions.DesiredLength.TotalSeconds; + } + } + + return picker; + } + + private Task GetMediaAsync(UIImagePickerControllerSourceType sourceType, string mediaType, StoreCameraMediaOptions options = null, CancellationToken token = default(CancellationToken)) + { + var viewController = GetHostViewController(); - if (token.IsCancellationRequested) - return Task.FromResult((MediaFile) null); + if (token.IsCancellationRequested) + return Task.FromResult((MediaFile)null); - var ndelegate = new MediaPickerDelegate(viewController, sourceType, options, token); - var od = Interlocked.CompareExchange(ref pickerDelegate, ndelegate, null); - if (od != null) - throw new InvalidOperationException("Only one operation can be active at a time"); + var ndelegate = new MediaPickerDelegate(viewController, sourceType, options, token); + var od = Interlocked.CompareExchange(ref pickerDelegate, ndelegate, null); + if (od != null) + throw new InvalidOperationException("Only one operation can be active at a time"); - var picker = SetupController(ndelegate, sourceType, mediaType, options); + var picker = SetupController(ndelegate, sourceType, mediaType, options); - if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && sourceType == UIImagePickerControllerSourceType.PhotoLibrary) - { - ndelegate.Popover = popover = new UIPopoverController(picker); - ndelegate.Popover.Delegate = new MediaPickerPopoverDelegate(ndelegate, picker); - ndelegate.DisplayPopover(); + if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && sourceType == UIImagePickerControllerSourceType.PhotoLibrary) + { + ndelegate.Popover = popover = new UIPopoverController(picker); + ndelegate.Popover.Delegate = new MediaPickerPopoverDelegate(ndelegate, picker); + ndelegate.DisplayPopover(); token.Register(() => { @@ -303,16 +303,16 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg ndelegate.CancelTask(); }); }); - } - else - { - if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) - { - picker.ModalPresentationStyle = options?.ModalPresentationStyle == MediaPickerModalPresentationStyle.OverFullScreen - ? UIModalPresentationStyle.OverFullScreen - : UIModalPresentationStyle.FullScreen; - } - viewController.PresentViewController(picker, true, null); + } + else + { + if (UIDevice.CurrentDevice.CheckSystemVersion(9, 0)) + { + picker.ModalPresentationStyle = options?.ModalPresentationStyle == MediaPickerModalPresentationStyle.OverFullScreen + ? UIModalPresentationStyle.OverFullScreen + : UIModalPresentationStyle.FullScreen; + } + viewController.PresentViewController(picker, true, null); token.Register(() => { @@ -320,20 +320,20 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg return; NSRunLoop.Main.BeginInvokeOnMainThread(() => - { + { picker.DismissModalViewController(true); ndelegate.CancelTask(); }); }); } - return ndelegate.Task.ContinueWith(t => - { + return ndelegate.Task.ContinueWith(t => + { Dismiss(popover, picker); return t.Result == null ? null : t.Result.FirstOrDefault(); }); - } + } private Task> GetMediasAsync(UIImagePickerControllerSourceType sourceType, string mediaType, StoreCameraMediaOptions options = null, MultiPickerOptions pickerOptions = null, CancellationToken token = default(CancellationToken)) { @@ -346,7 +346,7 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg var od = Interlocked.CompareExchange(ref pickerDelegate, ndelegate, null); if (od != null) throw new InvalidOperationException("Only one operation can be active at a time"); - + var picker = ELCImagePickerViewController.Create(options, pickerOptions); if (UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Pad && sourceType == UIImagePickerControllerSourceType.PhotoLibrary) @@ -363,7 +363,7 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg } viewController.PresentViewController(picker, true, null); } - + // TODO: Make this use the existing Delegate? return picker.Completion.ContinueWith(t => { @@ -372,7 +372,7 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg { picker.DismissViewController(true, null); }); - + if (t.IsCanceled || t.Exception != null) { return Task.FromResult(new List()); @@ -381,7 +381,7 @@ private static MediaPickerController SetupController(MediaPickerDelegate mpDeleg var files = t.Result; Parallel.ForEach(files, mediaFile => { - ResizeAndCompressImage(options, mediaFile, Path.GetExtension(mediaFile.Path)); + ResizeAndCompressImage(options, mediaFile, Path.GetExtension(mediaFile.Path).Replace(".", string.Empty)); }); return t; @@ -426,54 +426,41 @@ private static void ResizeAndCompressImage(StoreCameraMediaOptions options, Medi //begin resizing image image = image.ResizeImageWithAspectRatio(percent); } - - NSDictionary meta = null; - try - { - //meta = PhotoLibraryAccess.GetPhotoLibraryMetadata(asset.AssetUrl); - - //meta = info[UIImagePickerController.MediaMetadata] as NSDictionary; - if (meta != null && meta.ContainsKey(ImageIO.CGImageProperties.Orientation)) - { - var newMeta = new NSMutableDictionary(); - newMeta.SetValuesForKeysWithDictionary(meta); - var newTiffDict = new NSMutableDictionary(); - newTiffDict.SetValuesForKeysWithDictionary(meta[ImageIO.CGImageProperties.TIFFDictionary] as NSDictionary); - newTiffDict.SetValueForKey(meta[ImageIO.CGImageProperties.Orientation], ImageIO.CGImageProperties.TIFFOrientation); - newMeta[ImageIO.CGImageProperties.TIFFDictionary] = newTiffDict; - - meta = newMeta; - } - var location = options.Location; - if (meta != null && location != null) - { - meta = MediaPickerDelegate.SetGpsLocation(meta, location); - } - } - catch (Exception ex) - { - Console.WriteLine($"Unable to get metadata: {ex}"); - } - - //iOS quality is 0.0-1.0 - var quality = (options.CompressionQuality / 100f); - var savedImage = false; - if (meta != null) - savedImage = MediaPickerDelegate.SaveImageWithMetadata(image, quality, meta, mediaFile.Path, pathExtension); - - if (!savedImage) - image.AsJPEG(quality).Save(mediaFile.Path, true); - - image?.Dispose(); - image = null; - - GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); } catch (Exception ex) { Console.WriteLine($"Unable to compress image: {ex}"); } } + NSDictionary meta = null; + if (options.SaveMetaData) + { + try + { + meta = PhotoLibraryAccess.GetPhotoLibraryMetadata(new NSUrl(mediaFile.AlbumPath)); + } + catch (Exception ex) + { + Console.WriteLine($"Unable to get metadata: {ex}"); + } + } + //iOS quality is 0.0-1.0 + var quality = (options.CompressionQuality / 100f); + var savedImage = false; + if (meta != null) + savedImage = MediaPickerDelegate.SaveImageWithMetadata(image, quality, meta, mediaFile.Path, pathExtension); + + if (!savedImage) + { + if (pathExtension == "png") + image.AsPNG().Save(mediaFile.Path, true); + else + image.AsJPEG(quality).Save(mediaFile.Path, true); + } + + image?.Dispose(); + image = null; + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); } private void Dismiss(UIPopoverController popover, UIViewController picker) @@ -495,7 +482,7 @@ private void Dismiss(UIPopoverController popover, UIViewController picker) GC.Collect(GC.MaxGeneration, GCCollectionMode.Default); Interlocked.Exchange(ref pickerDelegate, null); } - + private static UIViewController GetHostViewController() { UIViewController viewController = null; @@ -522,39 +509,39 @@ private static UIViewController GetHostViewController() } private static UIImagePickerControllerCameraDevice GetUICameraDevice(CameraDevice device) - { - switch (device) - { - case CameraDevice.Front: - return UIImagePickerControllerCameraDevice.Front; - case CameraDevice.Rear: - return UIImagePickerControllerCameraDevice.Rear; - default: - throw new NotSupportedException(); - } - } - - private static UIImagePickerControllerQualityType GetQuailty(VideoQuality quality) - { - switch (quality) - { - case VideoQuality.Low: - return UIImagePickerControllerQualityType.Low; - case VideoQuality.Medium: - return UIImagePickerControllerQualityType.Medium; - default: - return UIImagePickerControllerQualityType.High; - } - } - - static async Task CheckPermissions(params Permission[] permissions) - { + { + switch (device) + { + case CameraDevice.Front: + return UIImagePickerControllerCameraDevice.Front; + case CameraDevice.Rear: + return UIImagePickerControllerCameraDevice.Rear; + default: + throw new NotSupportedException(); + } + } + + private static UIImagePickerControllerQualityType GetQuailty(VideoQuality quality) + { + switch (quality) + { + case VideoQuality.Low: + return UIImagePickerControllerQualityType.Low; + case VideoQuality.Medium: + return UIImagePickerControllerQualityType.Medium; + default: + return UIImagePickerControllerQualityType.High; + } + } + + static async Task CheckPermissions(params Permission[] permissions) + { //See which ones we need to request. var permissionsToRequest = new List(); - foreach(var permission in permissions) + foreach (var permission in permissions) { var permissionStatus = PermissionStatus.Unknown; - switch(permission) + switch (permission) { case Permission.Camera: permissionStatus = await CrossPermissions.Current.CheckPermissionStatusAsync(); @@ -569,7 +556,7 @@ static async Task CheckPermissions(params Permission[] permissions) if (permissionStatus != PermissionStatus.Granted) permissionsToRequest.Add(permission); - } + } //Nothing to request, Awesome! if (permissionsToRequest.Count == 0) @@ -599,16 +586,16 @@ static async Task CheckPermissions(params Permission[] permissions) //Gunna need those permissions :( throw new MediaPermissionException(notGranted.Select(r => r.Key).ToArray()); - - } + + } const string cameraDescription = "NSCameraUsageDescription"; const string photoDescription = "NSPhotoLibraryUsageDescription"; const string photoAddDescription = "NSPhotoLibraryAddUsageDescription"; const string microphoneDescription = "NSMicrophoneUsageDescription"; void CheckUsageDescription(params string[] descriptionNames) - { - foreach(var description in descriptionNames) + { + foreach (var description in descriptionNames) { var info = NSBundle.MainBundle.InfoDictionary; @@ -621,5 +608,5 @@ void CheckUsageDescription(params string[] descriptionNames) } } - } + } } diff --git a/src/Media.Plugin/iOS/MediaPickerDelegate.cs b/src/Media.Plugin/iOS/MediaPickerDelegate.cs index 1e9ed4d8..9944955f 100644 --- a/src/Media.Plugin/iOS/MediaPickerDelegate.cs +++ b/src/Media.Plugin/iOS/MediaPickerDelegate.cs @@ -47,12 +47,8 @@ public UIPopoverController Popover public Task> Task => tcs.Task; - private bool isFinished; public override async void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info) { - if (isFinished) - return; - isFinished = true; RemoveOrientationChangeObserverAndNotifications(); MediaFile mediaFile; @@ -81,7 +77,6 @@ public override async void FinishedPickingMedia(UIImagePickerController picker, tcs.SetException(new FileNotFoundException()); else tcs.TrySetResult(new List { mediaFile }); - isFinished = false; }); } @@ -132,7 +127,7 @@ public void DisplayPopover(bool hideFirst = false) if (IsValidInterfaceOrientation(UIDevice.CurrentDevice.Orientation)) orientation = UIDevice.CurrentDevice.Orientation; else - orientation = getDeviceOrientation(viewController.InterfaceOrientation); + orientation = GetDeviceOrientation(viewController.InterfaceOrientation); } double x, y; @@ -397,7 +392,7 @@ private async Task GetPictureMediaFile(NSDictionary info) if (!savedImage) { var finalQuality = quality; - var imageData = pathExtension == "jpg" ? image.AsJPEG(finalQuality) : image.AsPNG(); + var imageData = pathExtension == "png" ? image.AsPNG() : image.AsJPEG(finalQuality); //continue to move down quality , rare instances while (imageData == null && finalQuality > 0) @@ -480,7 +475,7 @@ internal static bool SaveImageWithMetadataiOS13(UIImage image, float quality, NS { pathExtension = pathExtension.ToLowerInvariant(); var finalQuality = quality; - var imageData = pathExtension == "jpg" ? image.AsJPEG(finalQuality) : image.AsPNG(); + var imageData = pathExtension == "png" ? image.AsPNG(): image.AsJPEG(finalQuality); //continue to move down quality , rare instances while (imageData == null && finalQuality > 0) @@ -497,10 +492,10 @@ internal static bool SaveImageWithMetadataiOS13(UIImage image, float quality, NS using var newImageSource = ciImage.CreateBySettingProperties(meta); using var ciContext = new CIContext(); - if (pathExtension == "jpg") - return ciContext.WriteJpegRepresentation(newImageSource, NSUrl.FromFilename(path), CGColorSpace.CreateSrgb(), new NSDictionary(), out var error); + if (pathExtension == "png") + return ciContext.WritePngRepresentation(newImageSource, NSUrl.FromFilename(path), CIFormat.ARGB8, CGColorSpace.CreateSrgb(), new NSDictionary(), out var error2); - return ciContext.WritePngRepresentation(newImageSource, NSUrl.FromFilename(path), CIFormat.ARGB8, CGColorSpace.CreateSrgb(), new NSDictionary(), out var error2); + return ciContext.WriteJpegRepresentation(newImageSource, NSUrl.FromFilename(path), CGColorSpace.CreateSrgb(), new NSDictionary(), out var error); } catch (Exception ex) { @@ -519,7 +514,7 @@ internal static bool SaveImageWithMetadata(UIImage image, float quality, NSDicti { pathExtension = pathExtension.ToLowerInvariant(); var finalQuality = quality; - var imageData = pathExtension == "jpg" ? image.AsJPEG(finalQuality) : image.AsPNG(); + var imageData = pathExtension == "png" ? image.AsPNG() : image.AsJPEG(finalQuality); //continue to move down quality , rare instances while (imageData == null && finalQuality > 0) @@ -684,6 +679,7 @@ static string GetUniquePath(string type, string path, string name, string pathEx internal static string GetOutputPath(string type, string path, string name, string extension, long index = 0) { + extension = extension.ToLowerInvariant(); path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), path); Directory.CreateDirectory(path); @@ -693,7 +689,7 @@ internal static string GetOutputPath(string type, string path, string name, stri if (string.IsNullOrWhiteSpace(name)) { if (type == MediaImplementation.TypeImage) - name = extension == "jpg" ? $"IMG_{postpendName}.jpg" : $"IMG_{postpendName}.png"; + name = extension == "png" ? $"IMG_{postpendName}.png" : $"IMG_{postpendName}.jpg"; else name = $"VID_{postpendName}.{extension ?? "mp4"}"; } @@ -727,7 +723,7 @@ private static bool IsSameOrientationKind(UIDeviceOrientation o1, UIDeviceOrient return false; } - private static UIDeviceOrientation getDeviceOrientation(UIInterfaceOrientation self) + private static UIDeviceOrientation GetDeviceOrientation(UIInterfaceOrientation self) { switch (self) { @@ -826,7 +822,7 @@ public static Stream RotateImage(UIImage image, int compressionQuality, string p pathExtension = pathExtension.ToLowerInvariant(); var finalQuality = pathExtension == "jpg" ? (compressionQuality / 100f) : 0f; - var imageData = pathExtension == "jpg" ? imageToReturn.AsJPEG(finalQuality) : imageToReturn.AsPNG(); + var imageData = pathExtension == "png" ? imageToReturn.AsPNG() : imageToReturn.AsJPEG(finalQuality); //continue to move down quality , rare instances while (imageData == null && finalQuality > 0) {