csharpc-sharpdotnetxamlavaloniauicross-platformcross-platform-xamlavaloniaguimulti-platformuser-interfacedotnetcore
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
8.7 KiB
248 lines
8.7 KiB
#if !TVOS
|
|
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Threading.Tasks;
|
|
using Avalonia.Controls;
|
|
using Avalonia.Logging;
|
|
using Avalonia.Platform.Storage;
|
|
using UIKit;
|
|
using Foundation;
|
|
using UniformTypeIdentifiers;
|
|
using UTTypeLegacy = MobileCoreServices.UTType;
|
|
using UTType = UniformTypeIdentifiers.UTType;
|
|
|
|
#nullable enable
|
|
|
|
namespace Avalonia.iOS.Storage;
|
|
|
|
internal class IOSStorageProvider : IStorageProvider
|
|
{
|
|
private readonly AvaloniaView _view;
|
|
public IOSStorageProvider(AvaloniaView view)
|
|
{
|
|
_view = view;
|
|
}
|
|
|
|
public bool CanOpen => true;
|
|
|
|
public bool CanSave => false;
|
|
|
|
public bool CanPickFolder => true;
|
|
|
|
public async Task<IReadOnlyList<IStorageFile>> OpenFilePickerAsync(FilePickerOpenOptions options)
|
|
{
|
|
UIDocumentPickerViewController documentPicker;
|
|
if (OperatingSystem.IsIOSVersionAtLeast(14))
|
|
{
|
|
var allowedUtils = options.FileTypeFilter?.SelectMany(f =>
|
|
{
|
|
// We check for OS version outside of the lambda, it's safe.
|
|
#pragma warning disable CA1416
|
|
if (f.TryGetExtensions() is { } extensions && extensions.Any())
|
|
{
|
|
return extensions.Select(UTType.CreateFromExtension);
|
|
}
|
|
if (f.AppleUniformTypeIdentifiers?.Any() == true)
|
|
{
|
|
return f.AppleUniformTypeIdentifiers.Select(UTType.CreateFromIdentifier);
|
|
}
|
|
if (f.MimeTypes?.Any() == true)
|
|
{
|
|
return f.MimeTypes.Select(UTType.CreateFromMimeType);
|
|
}
|
|
return Array.Empty<UTType>();
|
|
#pragma warning restore CA1416
|
|
})
|
|
.Where(id => id is not null)
|
|
.ToArray() ?? new[]
|
|
{
|
|
UTTypes.Content,
|
|
UTTypes.Item,
|
|
UTTypes.Data
|
|
};
|
|
documentPicker = new UIDocumentPickerViewController(allowedUtils!, false);
|
|
}
|
|
else
|
|
{
|
|
var allowedUtils = options.FileTypeFilter?.SelectMany(f => f.AppleUniformTypeIdentifiers ?? Array.Empty<string>())
|
|
.ToArray() ?? new[]
|
|
{
|
|
UTTypeLegacy.Content,
|
|
UTTypeLegacy.Item,
|
|
"public.data"
|
|
};
|
|
documentPicker = new UIDocumentPickerViewController(allowedUtils, UIDocumentPickerMode.Open);
|
|
}
|
|
|
|
using (documentPicker)
|
|
{
|
|
if (OperatingSystem.IsIOSVersionAtLeast(13))
|
|
{
|
|
documentPicker.DirectoryUrl = GetUrlFromFolder(options.SuggestedStartLocation);
|
|
}
|
|
|
|
if (OperatingSystem.IsIOSVersionAtLeast(11, 0))
|
|
{
|
|
documentPicker.AllowsMultipleSelection = options.AllowMultiple;
|
|
}
|
|
|
|
var urls = await ShowPicker(documentPicker);
|
|
return urls.Select(u => new IOSStorageFile(u)).ToArray();
|
|
}
|
|
}
|
|
|
|
public Task<IStorageBookmarkFile?> OpenFileBookmarkAsync(string bookmark)
|
|
{
|
|
return Task.FromResult<IStorageBookmarkFile?>(GetBookmarkedUrl(bookmark) is { } url
|
|
? new IOSStorageFile(url) : null);
|
|
}
|
|
|
|
public Task<IStorageBookmarkFolder?> OpenFolderBookmarkAsync(string bookmark)
|
|
{
|
|
return Task.FromResult<IStorageBookmarkFolder?>(GetBookmarkedUrl(bookmark) is { } url
|
|
? new IOSStorageFolder(url) : null);
|
|
}
|
|
|
|
public Task<IStorageFile?> TryGetFileFromPathAsync(Uri filePath)
|
|
{
|
|
// TODO: research if it's possible, maybe with additional permissions.
|
|
return Task.FromResult<IStorageFile?>(null);
|
|
}
|
|
|
|
public Task<IStorageFolder?> TryGetFolderFromPathAsync(Uri folderPath)
|
|
{
|
|
// TODO: research if it's possible, maybe with additional permissions.
|
|
return Task.FromResult<IStorageFolder?>(null);
|
|
}
|
|
|
|
public Task<IStorageFolder?> TryGetWellKnownFolderAsync(WellKnownFolder wellKnownFolder)
|
|
{
|
|
var directoryType = wellKnownFolder switch
|
|
{
|
|
WellKnownFolder.Desktop => NSSearchPathDirectory.DesktopDirectory,
|
|
WellKnownFolder.Documents => NSSearchPathDirectory.DocumentDirectory,
|
|
WellKnownFolder.Downloads => NSSearchPathDirectory.DownloadsDirectory,
|
|
WellKnownFolder.Music => NSSearchPathDirectory.MusicDirectory,
|
|
WellKnownFolder.Pictures => NSSearchPathDirectory.PicturesDirectory,
|
|
WellKnownFolder.Videos => NSSearchPathDirectory.MoviesDirectory,
|
|
_ => throw new ArgumentOutOfRangeException(nameof(wellKnownFolder), wellKnownFolder, null)
|
|
};
|
|
|
|
var uri = NSFileManager.DefaultManager.GetUrl(directoryType, NSSearchPathDomain.Local, null, true, out var error);
|
|
if (error != null)
|
|
{
|
|
throw new NSErrorException(error);
|
|
}
|
|
|
|
return Task.FromResult<IStorageFolder?>(new IOSStorageFolder(uri));
|
|
}
|
|
|
|
public Task<IStorageFile?> SaveFilePickerAsync(FilePickerSaveOptions options)
|
|
{
|
|
return Task.FromException<IStorageFile?>(
|
|
new PlatformNotSupportedException("Save file picker is not supported by iOS"));
|
|
}
|
|
|
|
public async Task<IReadOnlyList<IStorageFolder>> OpenFolderPickerAsync(FolderPickerOpenOptions options)
|
|
{
|
|
using var documentPicker = OperatingSystem.IsIOSVersionAtLeast(14) ?
|
|
new UIDocumentPickerViewController(new[] { UTTypes.Folder }, false) :
|
|
new UIDocumentPickerViewController(new string[] { UTTypeLegacy.Folder }, UIDocumentPickerMode.Open);
|
|
|
|
if (OperatingSystem.IsIOSVersionAtLeast(13))
|
|
{
|
|
documentPicker.DirectoryUrl = GetUrlFromFolder(options.SuggestedStartLocation);
|
|
}
|
|
|
|
if (OperatingSystem.IsIOSVersionAtLeast(11))
|
|
{
|
|
documentPicker.AllowsMultipleSelection = options.AllowMultiple;
|
|
}
|
|
|
|
var urls = await ShowPicker(documentPicker);
|
|
return urls.Select(u => new IOSStorageFolder(u)).ToArray();
|
|
}
|
|
|
|
private static NSUrl? GetUrlFromFolder(IStorageFolder? folder)
|
|
{
|
|
return folder switch
|
|
{
|
|
IOSStorageFolder iosFolder => iosFolder.Url,
|
|
null => null,
|
|
_ => folder.Path
|
|
};
|
|
}
|
|
|
|
private Task<NSUrl[]> ShowPicker(UIDocumentPickerViewController documentPicker)
|
|
{
|
|
var tcs = new TaskCompletionSource<NSUrl[]>();
|
|
documentPicker.Delegate = new PickerDelegate(urls => tcs.TrySetResult(urls));
|
|
|
|
if (documentPicker.PresentationController != null)
|
|
{
|
|
documentPicker.PresentationController.Delegate =
|
|
new UIPresentationControllerDelegate(() => tcs.TrySetResult(Array.Empty<NSUrl>()));
|
|
}
|
|
|
|
var controller = _view.Window?.RootViewController ?? throw new InvalidOperationException("RootViewController wasn't initialized");
|
|
controller.PresentViewController(documentPicker, true, null);
|
|
|
|
return tcs.Task;
|
|
}
|
|
|
|
private NSUrl? GetBookmarkedUrl(string bookmark)
|
|
{
|
|
var url = NSUrl.FromBookmarkData(new NSData(bookmark, NSDataBase64DecodingOptions.None),
|
|
NSUrlBookmarkResolutionOptions.WithoutUI, null, out var isStale, out var error);
|
|
if (isStale)
|
|
{
|
|
Logger.TryGet(LogEventLevel.Warning, LogArea.IOSPlatform)?.Log(this, "Stale bookmark detected");
|
|
}
|
|
|
|
if (error != null)
|
|
{
|
|
throw new NSErrorException(error);
|
|
}
|
|
return url;
|
|
}
|
|
|
|
private class PickerDelegate : UIDocumentPickerDelegate
|
|
{
|
|
private readonly Action<NSUrl[]>? _pickHandler;
|
|
|
|
internal PickerDelegate(Action<NSUrl[]> pickHandler)
|
|
=> _pickHandler = pickHandler;
|
|
|
|
public override void WasCancelled(UIDocumentPickerViewController controller)
|
|
=> _pickHandler?.Invoke(Array.Empty<NSUrl>());
|
|
|
|
public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl[] urls)
|
|
=> _pickHandler?.Invoke(urls);
|
|
|
|
public override void DidPickDocument(UIDocumentPickerViewController controller, NSUrl url)
|
|
=> _pickHandler?.Invoke(new[] { url });
|
|
}
|
|
|
|
private class UIPresentationControllerDelegate : UIAdaptivePresentationControllerDelegate
|
|
{
|
|
private Action? _dismissHandler;
|
|
|
|
internal UIPresentationControllerDelegate(Action dismissHandler)
|
|
=> this._dismissHandler = dismissHandler;
|
|
|
|
public override void DidDismiss(UIPresentationController presentationController)
|
|
{
|
|
_dismissHandler?.Invoke();
|
|
_dismissHandler = null;
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
_dismissHandler?.Invoke();
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|