Skip to content

Commit

Permalink
chore: PHAsset Attempt
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Dec 3, 2024
1 parent 735ee53 commit 7d3f20a
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 118 deletions.
52 changes: 7 additions & 45 deletions src/Uno.UWP/Devices/Sensors/Helpers/SensorHelpers.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,52 +1,14 @@
using Foundation;
using System;

namespace Uno.Devices.Sensors.Helpers
namespace Uno.Devices.Sensors.Helpers;

internal static class SensorHelpers
{
internal static class SensorHelpers
public static DateTimeOffset TimestampToDateTimeOffset(double timestamp)
{
private static readonly DateTimeOffset NSDateConversionStart =
new DateTimeOffset(2001, 1, 1, 0, 0, 0, TimeSpan.Zero);

public static DateTimeOffset TimestampToDateTimeOffset(double timestamp)
{
var bootTime = NSDate.FromTimeIntervalSinceNow(-NSProcessInfo.ProcessInfo.SystemUptime);
var date = (DateTime)bootTime.AddSeconds(timestamp);
return new DateTimeOffset(date);
}

public static DateTimeOffset NSDateToDateTimeOffset(NSDate nsDate)
{
if (nsDate == NSDate.DistantPast)
{
return DateTimeOffset.MinValue;
}
else if (nsDate == NSDate.DistantFuture)
{
return DateTimeOffset.MaxValue;
}

return NSDateConversionStart.AddSeconds(
nsDate.SecondsSinceReferenceDate);
}

public static NSDate DateTimeOffsetToNSDate(DateTimeOffset dateTimeOffset)
{
if (dateTimeOffset == DateTimeOffset.MinValue)
{
return NSDate.DistantPast;
}
else if (dateTimeOffset == DateTimeOffset.MaxValue)
{
return NSDate.DistantFuture;
}

var dateInSecondsFromStart = dateTimeOffset
.ToUniversalTime()
.Subtract(NSDateConversionStart.UtcDateTime);

return NSDate.FromTimeIntervalSinceReferenceDate(
dateInSecondsFromStart.TotalSeconds);
}
var bootTime = NSDate.FromTimeIntervalSinceNow(-NSProcessInfo.ProcessInfo.SystemUptime);
var date = (DateTime)bootTime.AddSeconds(timestamp);
return new DateTimeOffset(date);
}
}
48 changes: 48 additions & 0 deletions src/Uno.UWP/Extensions/NSDateExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Foundation;

namespace Windows.Extensions;

internal static class NSDateExtensions
{
private static readonly DateTimeOffset NSDateConversionStart =
new DateTimeOffset(2001, 1, 1, 0, 0, 0, TimeSpan.Zero);

public static DateTimeOffset ToDateTimeOffset(this NSDate nsDate)
{
if (nsDate == NSDate.DistantPast)
{
return DateTimeOffset.MinValue;
}
else if (nsDate == NSDate.DistantFuture)
{
return DateTimeOffset.MaxValue;
}

return NSDateConversionStart.AddSeconds(
nsDate.SecondsSinceReferenceDate);
}

public static NSDate ToNSDate(this DateTimeOffset dateTimeOffset)
{
if (dateTimeOffset == DateTimeOffset.MinValue)
{
return NSDate.DistantPast;
}
else if (dateTimeOffset == DateTimeOffset.MaxValue)
{
return NSDate.DistantFuture;
}

var dateInSecondsFromStart = dateTimeOffset
.ToUniversalTime()
.Subtract(NSDateConversionStart.UtcDateTime);

return NSDate.FromTimeIntervalSinceReferenceDate(
dateInSecondsFromStart.TotalSeconds);
}
}
77 changes: 49 additions & 28 deletions src/Uno.UWP/Storage/Pickers/FileOpenPicker.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,15 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Uno.Storage.Pickers.Internal;
using UIKit;
using Foundation;
using Windows.ApplicationModel.Core;
using Uno.Helpers.Theming;
using MobileCoreServices;
using Photos;
using PhotosUI;
using UIKit;
using Uno.Helpers.Theming;
using Uno.Storage.Pickers.Internal;
using Uno.UI.Dispatching;
using MobileCoreServices;
using Windows.Foundation.Metadata;
using Uno.Foundation.Logging;
using Windows.ApplicationModel.Core;

namespace Windows.Storage.Pickers
{
Expand Down Expand Up @@ -73,7 +72,7 @@ private UIViewController GetViewController(bool multiple, int limit, TaskComplet
};

case PickerLocationId.PicturesLibrary when multiple is true && iOS14AndAbove is true:
var imageConfiguration = new PHPickerConfiguration
var imageConfiguration = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary)
{
Filter = PHPickerFilter.ImagesFilter,
SelectionLimit = limit
Expand All @@ -83,7 +82,7 @@ private UIViewController GetViewController(bool multiple, int limit, TaskComplet
Delegate = new PhotoPickerDelegate(completionSource)
};
case PickerLocationId.VideosLibrary when multiple is true && iOS14AndAbove is true:
var videoConfiguration = new PHPickerConfiguration
var videoConfiguration = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary)
{
Filter = PHPickerFilter.VideosFilter,
SelectionLimit = limit
Expand Down Expand Up @@ -167,9 +166,13 @@ public override void FinishedPickingMedia(UIImagePickerController picker, NSDict
private class PhotoPickerDelegate : PHPickerViewControllerDelegate
{
private readonly TaskCompletionSource<StorageFile?[]> _taskCompletionSource;
private readonly bool _readOnly;

public PhotoPickerDelegate(TaskCompletionSource<StorageFile?[]> taskCompletionSource) =>
public PhotoPickerDelegate(TaskCompletionSource<StorageFile?[]> taskCompletionSource, bool readOnly)
{
_taskCompletionSource = taskCompletionSource;
_readOnly = readOnly;
}

public override async void DidFinishPicking(PHPickerViewController picker, PHPickerResult[] results)
{
Expand All @@ -186,33 +189,51 @@ public override async void DidFinishPicking(PHPickerViewController picker, PHPic
private async Task<IEnumerable<StorageFile>> ConvertPickerResults(PHPickerResult[] results)
{
List<StorageFile> storageFiles = new List<StorageFile>();
var providers = results
.Select(res => res.ItemProvider)
.Where(provider => provider != null && provider.RegisteredTypeIdentifiers?.Length > 0)
.ToArray();

foreach (NSItemProvider provider in providers)
if (_readOnly)
{
var identifier = GetIdentifier(provider.RegisteredTypeIdentifiers ?? []) ?? "public.data";
var data = await provider.LoadDataRepresentationAsync(identifier);

if (data is null)
var assetIdentifiers = results
.Select(res => res.AssetIdentifier!)
.Where(id => id != null)
.ToArray();

var resultsByIdentifier = results.ToDictionary(res => res.AssetIdentifier!);
var assets = PHAsset.FetchAssetsUsingLocalIdentifiers(assetIdentifiers, null);
foreach (PHAsset asset in assets)
{
continue;
var file = StorageFile.GetFromPHPickerResult(resultsByIdentifier[asset.LocalIdentifier], asset, null);
storageFiles.Add(file);
}
}
else
{
var providers = results
.Select(res => res.ItemProvider)
.Where(provider => provider != null && provider.RegisteredTypeIdentifiers?.Length > 0)
.ToArray();

var extension = GetExtension(identifier);
foreach (NSItemProvider provider in providers)
{
var identifier = GetIdentifier(provider.RegisteredTypeIdentifiers ?? []) ?? "public.data";
var data = await provider.LoadDataRepresentationAsync(identifier);

var destinationUrl = NSFileManager.DefaultManager
.GetTemporaryDirectory()
.Append($"{NSProcessInfo.ProcessInfo.GloballyUniqueString}.{extension}", false);
data.Save(destinationUrl, false);
if (data is null)
{
continue;
}

storageFiles.Add(StorageFile.GetFromSecurityScopedUrl(destinationUrl, null));
}
var extension = GetExtension(identifier);

var destinationUrl = NSFileManager.DefaultManager
.GetTemporaryDirectory()
.Append($"{NSProcessInfo.ProcessInfo.GloballyUniqueString}.{extension}", false);
data.Save(destinationUrl, false);

storageFiles.Add(StorageFile.GetFromSecurityScopedUrl(destinationUrl, null));
}
}
return storageFiles;
}

private static string? GetIdentifier(string[] identifiers)
{
if (!(identifiers?.Length > 0))
Expand Down
93 changes: 48 additions & 45 deletions src/Uno.UWP/Storage/StorageFile.iOS.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#nullable enable

using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Foundation;
using Photos;
using PhotosUI;
using UIKit;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;
using Uno.Storage.Internal;
using Uno.Storage.Streams.Internal;
using System.IO;
using System.Linq;
using MobileCoreServices;
using SystemPath = System.IO.Path;
using PhotosUI;
using Windows.Extensions;
using Windows.Storage.FileProperties;
using Windows.Storage.Streams;

namespace Windows.Storage
{
Expand All @@ -22,8 +22,8 @@ public partial class StorageFile
internal static StorageFile GetFromSecurityScopedUrl(NSUrl nsUrl, StorageFolder? parent) =>
new StorageFile(new SecurityScopedFile(nsUrl, parent));

internal static StorageFile GetFromPHPickerResult(PHPickerResult result, StorageFolder? parent) =>
new StorageFile(new PHPickerResultFile(result, parent));
internal static StorageFile GetFromPHPickerResult(PHPickerResult result, PHAsset phAsset, StorageFolder? parent) =>
new StorageFile(new PHPickerResultFile(result, phAsset, parent));

internal class SecurityScopedFile : ImplementationBase
{
Expand Down Expand Up @@ -100,61 +100,64 @@ implementation is SecurityScopedFile file &&
file._nsUrl.FilePathUrl?.Path == _nsUrl.FilePathUrl?.Path;
}

internal class PHPickerResultFile : ImplementationBase
internal class PHAssetFile : ImplementationBase
{
private PHPickerResult _phPickerResult;
private readonly PHAsset _phAsset;
private StorageFolder? _parent;

public PHPickerResultFile(PHPickerResult phPickerResult, StorageFolder? parent) : base(string.Empty)
public PHAssetFile(PHAsset phAsset, StorageFolder? parent) : base(string.Empty)
{
if (phPickerResult is null)
{
throw new ArgumentNullException(nameof(phPickerResult));
}

_phPickerResult = phPickerResult;
_phAsset = phAsset;
_parent = parent;
Path = phAsset.
}

public override StorageProvider Provider => StorageProviders.IosPHPicker;

public override DateTimeOffset DateCreated
{
get
{
var itemProvider = _phPickerResult.ItemProvider;
public override DateTimeOffset DateCreated => _phAsset.CreationDate.ToDateTimeOffset();

if (itemProvider.HasItemConformingTo(UTType.Image))
public override Task DeleteAsync(CancellationToken ct, StorageDeleteOption options)
{
TaskCompletionSource resultCompletionSource = new TaskCompletionSource();
PHPhotoLibrary.SharedPhotoLibrary.PerformChanges(
() =>
{
var fileUrl = await GetFileUrlAsync(itemProvider);

if (fileUrl != null)
PHAssetChangeRequest.DeleteAssets(new PHAsset[] { _phAsset });
},
(success, error) =>
{
if (success)
{
resultCompletionSource.SetResult();
}
else
{
var attributes = NSFileManager.DefaultManager.GetAttributes(fileUrl.Path, out NSError error);
if (error != null)
{
throw new IOException($"Error retrieving file attributes: {error.LocalizedDescription}");
}

var creationDate = attributes.CreationDate;
return creationDate?.ToDateTimeOffset()
?? throw new InvalidOperationException("Creation date not found.");
resultCompletionSource.SetException(new Exception(error.LocalizedDescription));
}
}
);

throw new InvalidOperationException("Item does not conform to a supported type.");
}
return resultCompletionSource.Task;
}

public override Task DeleteAsync(CancellationToken ct, StorageDeleteOption options) => throw new NotImplementedException();
public override Task<BasicProperties> GetBasicPropertiesAsync(CancellationToken ct) => throw new NotImplementedException();
public override Task<StorageFolder?> GetParentAsync(CancellationToken ct) => throw new NotImplementedException();
public override Task<IRandomAccessStreamWithContentType> OpenAsync(CancellationToken ct, FileAccessMode accessMode, StorageOpenOptions options) => throw new NotImplementedException();
public override Task<StorageStreamTransaction> OpenTransactedWriteAsync(CancellationToken ct, StorageOpenOptions option) => throw new NotImplementedException();
protected override bool IsEqual(ImplementationBase implementation)
public override Task<BasicProperties> GetBasicPropertiesAsync(CancellationToken ct)
{
implementation is PHPickerResultFile file && file._phPickerResult.ItemProvider.Load
var resources = PHAssetResource.GetAssetResources(_phAsset);
var imageSizeBytes = (resources.FirstOrDefault()?.ValueForKey(new NSString("fileSize")) as NSNumber) ?? 0;
var dateModified = _phAsset.ModificationDate.ToDateTimeOffset();
return Task.FromResult(new BasicProperties((ulong)imageSizeBytes, dateModified));
}

public override Task<StorageFolder?> GetParentAsync(CancellationToken ct) => Task.FromResult(_parent);

public override Task<IRandomAccessStreamWithContentType> OpenAsync(CancellationToken ct, FileAccessMode accessMode, StorageOpenOptions options)
=> Task.FromResult<IRandomAccessStreamWithContentType>(new RandomAccessStreamWithContentType(FileRandomAccessStream.CreateSecurityScoped(_phAsset.Rea, ToFileAccess(accessMode), ToFileShare(options)), ContentType));
public override Task<StorageStreamTransaction> OpenTransactedWriteAsync(CancellationToken ct, StorageOpenOptions option)
{

}

protected override bool IsEqual(ImplementationBase implementation) => implementation is PHAssetFile file && file._phAsset.LocalIdentifier == _phAsset.LocalIdentifier;
}
}
}

0 comments on commit 7d3f20a

Please sign in to comment.