Browse Source

Add folder.GetItemsAsync API

pull/8458/head
Max Katz 4 years ago
parent
commit
97a5a9e1f6
  1. 16
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  2. 25
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  3. 12
      src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs
  4. 11
      src/Avalonia.Base/Platform/Storage/IStorageFolder.cs
  5. 25
      src/Web/Avalonia.Web.Blazor/Interop/Storage/StorageProviderInterop.cs
  6. 45
      src/Web/Avalonia.Web.Blazor/Interop/Typescript/StorageProvider.ts
  7. 17
      src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs

16
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -243,8 +243,8 @@ namespace ControlCatalog.Pages
async Task SetPickerResult(IReadOnlyCollection<IStorageItem>? items)
{
items ??= Array.Empty<IStorageItem>();
var mappedResults = items.Select(FullPathOrName).ToList();
bookmarkContainer.Text = items.FirstOrDefault(f => f.CanBookmark) is { } f ? await f.SaveBookmark() : "Can't bookmark";
var mappedResults = new List<string>();
if (items.FirstOrDefault() is IStorageItem item)
{
@ -293,7 +293,19 @@ Content:
lastSelectedDirectory = await item.GetParentAsync();
if (lastSelectedDirectory is not null)
{
mappedResults.Insert(0, "Parent: " + FullPathOrName(lastSelectedDirectory));
mappedResults.Add(FullPathOrName(lastSelectedDirectory));
}
foreach (var selectedItem in items)
{
mappedResults.Add("+> " + FullPathOrName(selectedItem));
if (selectedItem is IStorageFolder folder)
{
foreach (var innerItems in await folder.GetItemsAsync())
{
mappedResults.Add("++> " + FullPathOrName(innerItems));
}
}
}
}

25
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -1,6 +1,7 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
@ -106,6 +107,30 @@ internal sealed class AndroidStorageFolder : AndroidStorageItem, IStorageBookmar
{
return Task.FromResult(new StorageItemProperties());
}
public async Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
{
using var javaFile = new JavaFile(Uri.Path!);
// Java file represents files AND directories. Don't be confused.
var files = await javaFile.ListFilesAsync().ConfigureAwait(false);
if (files is null)
{
return Array.Empty<IStorageItem>();
}
return files
.Select(f => (file: f, uri: AndroidUri.FromFile(f)))
.Where(t => t.uri is not null)
.Select(t => t.file switch
{
{ IsFile: true } => (IStorageItem)new AndroidStorageFile(Context, t.uri!),
{ IsDirectory: true } => new AndroidStorageFolder(Context, t.uri!),
_ => null
})
.Where(i => i is not null)
.ToArray()!;
}
}
internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkFile

12
src/Avalonia.Base/Platform/Storage/FileIO/BclStorageFolder.cs

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Security;
using System.Threading.Tasks;
using Avalonia.Metadata;
@ -43,6 +45,16 @@ public class BclStorageFolder : IStorageBookmarkFolder
return Task.FromResult<IStorageFolder?>(null);
}
public Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
{
var items = _directoryInfo.GetDirectories()
.Select(d => (IStorageItem)new BclStorageFolder(d))
.Concat(_directoryInfo.GetFiles().Select(f => new BclStorageFile(f)))
.ToArray();
return Task.FromResult<IReadOnlyList<IStorageItem>>(items);
}
public virtual Task<string?> SaveBookmark()
{
return Task.FromResult<string?>(_directoryInfo.FullName);

11
src/Avalonia.Base/Platform/Storage/IStorageFolder.cs

@ -1,4 +1,6 @@
using Avalonia.Metadata;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Metadata;
namespace Avalonia.Platform.Storage;
@ -8,4 +10,11 @@ namespace Avalonia.Platform.Storage;
[NotClientImplementable]
public interface IStorageFolder : IStorageItem
{
/// <summary>
/// Gets the files and subfolders in the current folder.
/// </summary>
/// <returns>
/// When this method completes successfully, it returns a list of the files and folders in the current folder. Each item in the list is represented by an <see cref="IStorageItem"/> implementation object.
/// </returns>
Task<IReadOnlyList<IStorageItem>> GetItemsAsync();
}

25
src/Web/Avalonia.Web.Blazor/Interop/Storage/StorageProviderInterop.cs

@ -196,5 +196,30 @@ namespace Avalonia.Web.Blazor.Interop.Storage
public JSStorageFolder(IJSInProcessObjectReference fileHandle) : base(fileHandle)
{
}
public async Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
{
var items = await FileHandle.InvokeAsync<IJSInProcessObjectReference?>("getItems");
if (items is null)
{
return Array.Empty<IStorageItem>();
}
var count = items.Invoke<int>("count");
return Enumerable.Range(0, count)
.Select(index =>
{
var reference = items.Invoke<IJSInProcessObjectReference>("at", index);
return reference.Invoke<string>("getKind") switch
{
"directory" => (IStorageItem)new JSStorageFolder(reference),
"file" => new JSStorageFile(reference),
_ => null
};
})
.Where(i => i is not null)
.ToArray()!;
}
}
}

45
src/Web/Avalonia.Web.Blazor/Interop/Typescript/StorageProvider.ts

@ -14,6 +14,8 @@ declare global {
queryPermission(options?: { mode: PermissionsMode }): Promise<"granted" | "denied" | "prompt">;
requestPermission(options?: { mode: PermissionsMode }): Promise<"granted" | "denied" | "prompt">;
entries(): AsyncIterableIterator<[string, FileSystemFileHandle]>;
}
type WellKnownDirectory = "desktop" | "documents" | "downloads" | "music" | "pictures" | "videos";
type StartInDirectory = WellKnownDirectory | FileSystemFileHandle;
@ -53,7 +55,7 @@ class IndexedDbWrapper {
}
public connect(): Promise<InnerDbConnection> {
var conn = window.indexedDB.open(this.databaseName, 1);
const conn = window.indexedDB.open(this.databaseName, 1);
conn.onupgradeneeded = event => {
const db = (<IDBRequest<IDBDatabase>>event.target).result;
@ -85,7 +87,7 @@ class InnerDbConnection {
const os = this.openStore(store, "readwrite");
return new Promise((resolve, reject) => {
var response = os.put(obj, key);
const response = os.put(obj, key);
response.onsuccess = () => {
resolve(response.result);
};
@ -99,7 +101,7 @@ class InnerDbConnection {
const os = this.openStore(store, "readonly");
return new Promise((resolve, reject) => {
var response = os.get(key);
const response = os.get(key);
response.onsuccess = () => {
resolve(response.result);
};
@ -113,7 +115,7 @@ class InnerDbConnection {
const os = this.openStore(store, "readwrite");
return new Promise((resolve, reject) => {
var response = os.delete(key);
const response = os.delete(key);
response.onsuccess = () => {
resolve();
};
@ -134,17 +136,20 @@ const avaloniaDb = new IndexedDbWrapper("AvaloniaDb", [
])
class StorageItem {
constructor(private handle: FileSystemFileHandle, private bookmarkId?: string) { }
constructor(public handle: FileSystemFileHandle, private bookmarkId?: string) { }
public getName(): string {
return this.handle.name
}
public getKind(): string {
return this.handle.kind;
}
public async openRead(): Promise<Blob> {
await this.verityPermissions('read');
var file = await this.handle.getFile();
return file;
return await this.handle.getFile();
}
public async openWrite(): Promise<FileSystemWritableFileStream> {
@ -154,7 +159,7 @@ class StorageItem {
}
public async getProperties(): Promise<{ Size: number, LastModified: number, Type: string }> {
var file = this.handle.getFile && await this.handle.getFile();
const file = this.handle.getFile && await this.handle.getFile();
return file && {
Size: file.size,
@ -163,6 +168,18 @@ class StorageItem {
}
}
public async getItems(): Promise<StorageItems> {
if (this.handle.kind !== "directory"){
return new StorageItems([]);
}
const items: StorageItem[] = [];
for await (const [key, value] of this.handle.entries()) {
items.push(new StorageItem(value));
}
return new StorageItems(items);
}
private async verityPermissions(mode: PermissionsMode): Promise<void | never> {
if (await this.handle.queryPermission({ mode }) === 'granted') {
return;
@ -235,12 +252,12 @@ export class StorageProvider {
}
public static async selectFolderDialog(
startIn: StartInDirectory | null)
startIn: StorageItem | null)
: Promise<StorageItem> {
// 'Picker' API doesn't accept "null" as a parameter, so it should be set to undefined.
const options: DirectoryPickerOptions = {
startIn: (startIn || undefined)
startIn: (startIn?.handle || undefined)
};
const handle = await window.showDirectoryPicker(options);
@ -248,12 +265,12 @@ export class StorageProvider {
}
public static async openFileDialog(
startIn: StartInDirectory | null, multiple: boolean,
startIn: StorageItem | null, multiple: boolean,
types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean)
: Promise<StorageItems> {
const options: OpenFilePickerOptions = {
startIn: (startIn || undefined),
startIn: (startIn?.handle || undefined),
multiple,
excludeAcceptAllOption,
types: (types || undefined)
@ -264,12 +281,12 @@ export class StorageProvider {
}
public static async saveFileDialog(
startIn: StartInDirectory | null, suggestedName: string | null,
startIn: StorageItem | null, suggestedName: string | null,
types: FilePickerAcceptType[] | null, excludeAcceptAllOption: boolean)
: Promise<StorageItem> {
const options: SaveFilePickerOptions = {
startIn: (startIn || undefined),
startIn: (startIn?.handle || undefined),
suggestedName: (suggestedName || undefined),
excludeAcceptAllOption,
types: (types || undefined)

17
src/iOS/Avalonia.iOS/Storage/IOSStorageItem.cs

@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Logging;
using Avalonia.Platform.Storage;
@ -118,4 +120,19 @@ internal sealed class IOSStorageFolder : IOSStorageItem, IStorageBookmarkFolder
public IOSStorageFolder(NSUrl url) : base(url)
{
}
public Task<IReadOnlyList<IStorageItem>> GetItemsAsync()
{
var content = NSFileManager.DefaultManager.GetDirectoryContent(Url, null, NSDirectoryEnumerationOptions.None, out var error);
if (error is not null)
{
return Task.FromException<IReadOnlyList<IStorageItem>>(new NSErrorException(error));
}
var items = content
.Select(u => u.HasDirectoryPath ? (IStorageItem)new IOSStorageFolder(u) : new IOSStorageFile(u))
.ToArray();
return Task.FromResult<IReadOnlyList<IStorageItem>>(items);
}
}

Loading…
Cancel
Save