Skip to content

Commit

Permalink
chore: Open file with temporary copy
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinZikmund committed Dec 3, 2024
1 parent 7d3f20a commit 4ceea06
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 34 deletions.
32 changes: 4 additions & 28 deletions src/Uno.UWP/Storage/Pickers/FileOpenPicker.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ private UIViewController GetViewController(bool multiple, int limit, TaskComplet
};
return new PHPickerViewController(imageConfiguration)
{
Delegate = new PhotoPickerDelegate(completionSource)
Delegate = new PhotoPickerDelegate(completionSource, _isReadOnly)
};
case PickerLocationId.VideosLibrary when multiple is true && iOS14AndAbove is true:
var videoConfiguration = new PHPickerConfiguration(PHPhotoLibrary.SharedPhotoLibrary)
Expand All @@ -89,7 +89,7 @@ private UIViewController GetViewController(bool multiple, int limit, TaskComplet
};
return new PHPickerViewController(videoConfiguration)
{
Delegate = new PhotoPickerDelegate(completionSource)
Delegate = new PhotoPickerDelegate(completionSource, _isReadOnly)
};

default:
Expand Down Expand Up @@ -213,15 +213,15 @@ private async Task<IEnumerable<StorageFile>> ConvertPickerResults(PHPickerResult

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

if (data is null)
{
continue;
}

var extension = GetExtension(identifier);
var extension = StorageFile.GetUTFileExtension(identifier);

var destinationUrl = NSFileManager.DefaultManager
.GetTemporaryDirectory()
Expand All @@ -233,30 +233,6 @@ private async Task<IEnumerable<StorageFile>> ConvertPickerResults(PHPickerResult
}
return storageFiles;
}

private static string? GetIdentifier(string[] identifiers)
{
if (!(identifiers?.Length > 0))
{
return null;
}

if (identifiers.Any(i => i.StartsWith(UTType.LivePhoto, StringComparison.InvariantCultureIgnoreCase)) && identifiers.Contains(UTType.JPEG))
{
return identifiers.FirstOrDefault(i => i == UTType.JPEG);
}

if (identifiers.Contains(UTType.QuickTimeMovie))
{
return identifiers.FirstOrDefault(i => i == UTType.QuickTimeMovie);
}

return identifiers.FirstOrDefault();
}

private string? GetExtension(string identifier)
=> UTType.CopyAllTags(identifier, UTType.TagClassFilenameExtension)?.FirstOrDefault();

}

private class FileOpenPickerDelegate : UIDocumentPickerDelegate
Expand Down
86 changes: 80 additions & 6 deletions src/Uno.UWP/Storage/StorageFile.iOS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Foundation;
using MobileCoreServices;
using Photos;
using PhotosUI;
using UIKit;
Expand All @@ -23,7 +24,7 @@ internal static StorageFile GetFromSecurityScopedUrl(NSUrl nsUrl, StorageFolder?
new StorageFile(new SecurityScopedFile(nsUrl, parent));

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

internal class SecurityScopedFile : ImplementationBase
{
Expand Down Expand Up @@ -103,13 +104,19 @@ implementation is SecurityScopedFile file &&
internal class PHAssetFile : ImplementationBase
{
private readonly PHAsset _phAsset;
private readonly PHPickerResult _pickerResult;
private StorageFolder? _parent;

public PHAssetFile(PHAsset phAsset, StorageFolder? parent) : base(string.Empty)
private NSUrl? _fileUrl;

public PHAssetFile(PHAsset phAsset, PHPickerResult pickerResult, StorageFolder? parent) : base(string.Empty)
{
_phAsset = phAsset;
_pickerResult = pickerResult;
_parent = parent;
Path = phAsset.

var resources = PHAssetResource.GetAssetResources(_phAsset);
Path = ((PHAssetResource)resources[0]).OriginalFilename;
}

public override StorageProvider Provider => StorageProviders.IosPHPicker;
Expand Down Expand Up @@ -150,14 +157,81 @@ public override Task<BasicProperties> GetBasicPropertiesAsync(CancellationToken

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)
public override async Task<IRandomAccessStreamWithContentType> OpenAsync(CancellationToken ct, FileAccessMode accessMode, StorageOpenOptions options)
{
await EnsureLoadedAsync();

if (_fileUrl is null)
{
throw new InvalidOperationException("The file could not be loaded.");
}

return new RandomAccessStreamWithContentType(FileRandomAccessStream.CreateSecurityScoped(_fileUrl, ToFileAccess(accessMode), ToFileShare(options)), ContentType);
}

public override async Task<Stream> OpenStreamAsync(CancellationToken ct, FileAccessMode accessMode, StorageOpenOptions options)
{
await EnsureLoadedAsync();

if (_fileUrl is null)
{
throw new InvalidOperationException("The file could not be loaded.");
}

Func<Stream> streamBuilder = () => File.Open(Path, FileMode.Open, ToFileAccess(accessMode), ToFileShare(options));
var streamWrapper = new SecurityScopeStreamWrapper(_fileUrl, streamBuilder);
return streamWrapper;
}

public override Task<StorageStreamTransaction> OpenTransactedWriteAsync(CancellationToken ct, StorageOpenOptions option) => throw new NotSupportedException();

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

private async Task EnsureLoadedAsync()
{
if (_fileUrl is not null)
{
return;
}

var identifier = GetUTIdentifier(_pickerResult.ItemProvider.RegisteredTypeIdentifiers ?? []) ?? "public.data";
var extension = GetUTFileExtension(identifier);
var result = await _pickerResult.ItemProvider.LoadFileRepresentationAsync(identifier);
if (result != null)
{
var destinationUrl = NSFileManager.DefaultManager
.GetTemporaryDirectory()
.Append($"{NSProcessInfo.ProcessInfo.GloballyUniqueString}.{extension}", false);

using Stream source = File.Open(result.Path!, FileMode.Open);
using Stream destination = File.Create(destinationUrl.Path!);
await source.CopyToAsync(destination);

_fileUrl = destinationUrl;
}
}
}

internal static string? GetUTIdentifier(string[] identifiers)
{
if (!(identifiers?.Length > 0))
{
return null;
}

if (identifiers.Any(i => i.StartsWith(UTType.LivePhoto, StringComparison.InvariantCultureIgnoreCase)) && identifiers.Contains(UTType.JPEG))
{
return identifiers.FirstOrDefault(i => i == UTType.JPEG);
}

if (identifiers.Contains(UTType.QuickTimeMovie))
{
return identifiers.FirstOrDefault(i => i == UTType.QuickTimeMovie);
}

return identifiers.FirstOrDefault();
}

internal static string? GetUTFileExtension(string identifier) => UTType.CopyAllTags(identifier, UTType.TagClassFilenameExtension)?.FirstOrDefault();
}
}

0 comments on commit 4ceea06

Please sign in to comment.