Skip to content

Commit fc45b5b

Browse files
committed
Added DeferredSaveFileOptions option.
1 parent 2b29ebf commit fc45b5b

12 files changed

Lines changed: 132 additions & 97 deletions

LukeMauiFilePicker.Demo/LukeMauiFilePicker.Demo.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@
5050

5151
<ItemGroup>
5252
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
53+
<PackageReference Include="Microsoft.Maui.Controls" Version="8.0.100" />
5354
</ItemGroup>
5455

55-
<ItemGroup>
56-
<ProjectReference Include="..\LukeMauiFilePicker\LukeMauiFilePicker.csproj" />
56+
<ItemGroup>
57+
<ProjectReference Include="..\LukeMauiFilePicker\LukeMauiFilePicker.csproj" />
5758
</ItemGroup>
5859

5960
</Project>

LukeMauiFilePicker.Demo/MainPage.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<Editor x:Name="TextEditor" HeightRequest="200" Margin="10" />
1111

1212
<Button Text="Save" Clicked="Save" Margin="10" />
13+
<Button Text="Deferred Save" Clicked="DeferredSave" Margin="10" />
1314
</VerticalStackLayout>
1415

1516
</ContentPage>

LukeMauiFilePicker.Demo/MainPage.xaml.cs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,19 +62,41 @@ private async void PickMany(object sender, EventArgs e)
6262
await OnFilesPickedAsync(files);
6363
}
6464

65-
private async void Save(object sender, EventArgs e)
65+
MemoryStream GetContentStream()
6666
{
6767
var bytes = Encoding.UTF8.GetBytes(TextEditor.Text ?? "");
68-
using var memory = new MemoryStream(bytes);
68+
return new MemoryStream(bytes);
69+
}
70+
71+
private async void Save(object sender, EventArgs e)
72+
{
73+
using var memory = GetContentStream();
6974

70-
var result = await picker.SaveFileAsync(new("text.txt", memory)
75+
var result = await picker.SaveFileAsync(new SaveFileOptions("text.txt", memory)
7176
{
7277
AndroidMimeType = "text/plain",
73-
WindowsFileTypes = ("Text files", new() { ".txt", })
78+
WindowsFileTypes = ("Text files", [".txt",])
7479
});
7580

7681
await DisplayAlert("File Saved", result ? "File saved successfully." : "File save cancelled.", "OK");
7782
}
7883

84+
private async void DeferredSave(object sender, EventArgs e)
85+
{
86+
// Simulate generating content
87+
async Task<Stream> streamFn()
88+
{
89+
await Task.Delay(2000);
90+
return GetContentStream();
91+
}
92+
93+
var result = await picker.SaveFileAsync(new DeferredSaveFileOptions("deferred.txt", streamFn)
94+
{
95+
AndroidMimeType = "text/plain",
96+
WindowsFileTypes = ("Text files", [".txt",])
97+
});
98+
99+
await DisplayAlert("File Saved", result ? "File saved successfully." : "File save cancelled.", "OK");
100+
}
79101
}
80102

LukeMauiFilePicker/CreateDocumentService.Android.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal class CreateDocumentService
1515

1616
TaskCompletionSource<Uri?>? tcs;
1717

18-
public async Task<Uri?> RequestSaveFileAsync(SaveFileOptions o, Activity? activity)
18+
public async Task<Uri?> RequestSaveFileAsync(BaseSaveFileOptions o, Activity? activity)
1919
{
2020
if (!RegisteredForActivityResult)
2121
{
@@ -50,7 +50,7 @@ public void OnActivityResult(Activity _, int requestCode, Result resultCode, Int
5050
tcs = null;
5151
}
5252

53-
Intent GetIntent(SaveFileOptions o)
53+
Intent GetIntent(BaseSaveFileOptions o)
5454
{
5555
var i = new Intent(Intent.ActionCreateDocument);
5656
i.AddCategory(Intent.CategoryOpenable);

LukeMauiFilePicker/FilePickerModels.cs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,34 @@ public interface IPickFile
1010

1111
}
1212

13-
public class SaveFileOptions
13+
public class BaseSaveFileOptions(string suggestedFileName)
1414
{
15-
16-
public string SuggestedFileName { get; init; }
17-
public Stream Content { get; init; }
18-
public (string FileTypeName, List<string> FileTypeExts) WindowsFileTypes { get; init; } = ("All Files", new() { "*.*" });
15+
public string SuggestedFileName { get; init; } = suggestedFileName;
16+
public (string FileTypeName, List<string> FileTypeExts) WindowsFileTypes { get; init; } = ("All Files", ["*.*"]);
1917
public string AndroidMimeType { get; init; } = "application/octet-stream";
18+
}
19+
20+
public class SaveFileOptions(string suggestedFileName, Stream content) : BaseSaveFileOptions(suggestedFileName)
21+
{
22+
23+
public Stream Content { get; init; } = content;
24+
}
2025

21-
public SaveFileOptions(string fileName, Stream content)
26+
public class DeferredSaveFileOptions : BaseSaveFileOptions
27+
{
28+
private readonly Func<Task<Stream>> _getStreamAsync;
29+
30+
public DeferredSaveFileOptions(string suggestedFileName, Func<Stream> getStream)
31+
: base(suggestedFileName)
32+
{
33+
_getStreamAsync = () => Task.FromResult(getStream());
34+
}
35+
36+
public DeferredSaveFileOptions(string suggestedFileName, Func<Task<Stream>> getStreamAsync)
37+
: base(suggestedFileName)
2238
{
23-
SuggestedFileName = fileName;
24-
Content = content;
39+
_getStreamAsync = getStreamAsync;
2540
}
41+
42+
public Task<Stream> GetContentAsync() => _getStreamAsync();
2643
}

LukeMauiFilePicker/FilePickerService.Android.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,37 @@ partial class FilePickerService
1212
public partial async Task<IEnumerable<IPickFile>?> PickFilesAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types, bool multiple)
1313
=> await DefaultPickFilesAsync(title, types, multiple);
1414

15-
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
15+
static async Task<Stream?> GetSaveStreamAsync(BaseSaveFileOptions o)
1616
{
1717
var activity = await Platform.WaitForActivityAsync();
1818
var uri = await CreateDocumentService.Instance
19-
.RequestSaveFileAsync(options, activity);
20-
if (uri is null) { return false; }
21-
19+
.RequestSaveFileAsync(o, activity);
20+
if (uri is null) { return null; }
21+
2222
ArgumentNullException.ThrowIfNull(activity?.ContentResolver);
2323

24-
using var outStream = activity.ContentResolver.OpenOutputStream(uri);
24+
var outStream = activity.ContentResolver.OpenOutputStream(uri);
2525
ArgumentNullException.ThrowIfNull(outStream);
2626

27+
return outStream;
28+
}
29+
30+
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
31+
{
32+
await using var outStream = await GetSaveStreamAsync(options);
33+
if (outStream is null) { return false; }
34+
2735
await options.Content.CopyToAsync(outStream);
36+
return true;
37+
}
38+
39+
public partial async Task<bool> SaveFileAsync(DeferredSaveFileOptions options)
40+
{
41+
using var outStream = await GetSaveStreamAsync(options);
42+
if (outStream is null) { return false; }
2843

44+
var content = await options.GetContentAsync();
45+
await content.CopyToAsync(outStream);
2946
return true;
3047
}
3148

LukeMauiFilePicker/FilePickerService.MaciOS.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Foundation;
2+
using ObjCRuntime;
23
using UIKit;
34
using UniformTypeIdentifiers;
45

@@ -58,16 +59,16 @@ partial class FilePickerService
5859
return await filesTcs.Task;
5960
}
6061

61-
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
62+
static async Task<bool> SaveFileAsync(BaseSaveFileOptions options, Stream stream)
6263
{
6364
var path = Path.Combine(FileSystem.CacheDirectory, options.SuggestedFileName);
6465
using (var file = File.Create(path))
6566
{
66-
await options.Content.CopyToAsync(file);
67+
await stream.CopyToAsync(file);
6768
}
6869
var url = new NSUrl(path, false);
6970

70-
var picker = new UIDocumentPickerViewController(new[] { url });
71+
var picker = new UIDocumentPickerViewController([url]);
7172
TaskCompletionSource<bool> filesTcs = new();
7273

7374
picker.WasCancelled += (_, e) =>
@@ -87,6 +88,17 @@ public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
8788
return await filesTcs.Task;
8889
}
8990

91+
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
92+
{
93+
return await SaveFileAsync(options, options.Content);
94+
}
95+
96+
public partial async Task<bool> SaveFileAsync(DeferredSaveFileOptions options)
97+
{
98+
await using var stream = await options.GetContentAsync();
99+
return await SaveFileAsync(options, stream);
100+
}
101+
90102
class IosFile : IPickFile
91103
{
92104

LukeMauiFilePicker/FilePickerService.Windows.cs

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,39 @@ namespace LukeMauiFilePicker;
55

66
partial class FilePickerService
77
{
8-
8+
99
public partial async Task<IEnumerable<IPickFile>?> PickFilesAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types, bool multiple)
1010
=> await DefaultPickFilesAsync(title, types, multiple);
1111

12-
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
12+
static async Task<StorageFile?> GetSaveStorageFileAsync(BaseSaveFileOptions options)
1313
{
1414
var picker = CreatePicker();
15-
if (picker is null) { return false; }
15+
if (picker is null) { return null; }
1616

1717
picker.FileTypeChoices.Add(options.WindowsFileTypes.FileTypeName, options.WindowsFileTypes.FileTypeExts);
1818
picker.SuggestedFileName = options.SuggestedFileName;
1919

2020
var file = await picker.PickSaveFileAsync();
21+
return file;
22+
}
23+
24+
public partial async Task<bool> SaveFileAsync(SaveFileOptions options)
25+
{
26+
var file = await GetSaveStorageFileAsync(options);
2127
if (file is null) { return false; }
2228

2329
return await WriteToFileAsync(file, options.Content);
2430
}
2531

32+
public partial async Task<bool> SaveFileAsync(DeferredSaveFileOptions options)
33+
{
34+
var file = await GetSaveStorageFileAsync(options);
35+
if (file is null) { return false; }
36+
37+
await using var content = await options.GetContentAsync();
38+
return await WriteToFileAsync(file, content);
39+
}
40+
2641
static async Task<bool> WriteToFileAsync(IStorageFile file, Stream content)
2742
{
2843
CachedFileManager.DeferUpdates(file);

LukeMauiFilePicker/FilePickerService.cs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ public interface IFilePickerService
55
Task<IPickFile?> PickFileAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types);
66
Task<IEnumerable<IPickFile>?> PickFilesAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types, bool multiple);
77
Task<bool> SaveFileAsync(SaveFileOptions options);
8+
Task<bool> SaveFileAsync(DeferredSaveFileOptions options);
89
}
910

1011

@@ -20,6 +21,7 @@ public partial class FilePickerService : IFilePickerService
2021
}
2122
public partial Task<IEnumerable<IPickFile>?> PickFilesAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types, bool multiple);
2223
public partial Task<bool> SaveFileAsync(SaveFileOptions options);
24+
public partial Task<bool> SaveFileAsync(DeferredSaveFileOptions options);
2325

2426
static async Task<IEnumerable<IPickFile>?> DefaultPickFilesAsync(string title, Dictionary<DevicePlatform, IEnumerable<string>>? types, bool multiple)
2527
{
@@ -51,15 +53,8 @@ public partial class FilePickerService : IFilePickerService
5153
.ToList();
5254
}
5355

54-
class DefaultPickFile : IPickFile
56+
class DefaultPickFile(FileResult file) : IPickFile
5557
{
56-
57-
readonly FileResult file;
58-
public DefaultPickFile(FileResult file)
59-
{
60-
this.file = file;
61-
}
62-
6358
public string FileName => file.FileName;
6459

6560
public FileResult? FileResult => file;

LukeMauiFilePicker/LukeMauiFilePicker.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<SingleProject>true</SingleProject>
88
<ImplicitUsings>enable</ImplicitUsings>
99
<Nullable>enable</Nullable>
10+
<SkipValidateMauiImplicitPackageReferences>true</SkipValidateMauiImplicitPackageReferences>
1011

1112
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">14.2</SupportedOSPlatformVersion>
1213
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">14.0</SupportedOSPlatformVersion>
@@ -23,7 +24,7 @@
2324
<RepositoryType>git</RepositoryType>
2425
<PackageTags>android;windows;ios;picker;file picker;maui;winui;maccatalyst</PackageTags>
2526
<PackageLicenseExpression>MIT</PackageLicenseExpression>
26-
<Version>1.1.1</Version>
27+
<Version>1.2.0</Version>
2728
</PropertyGroup>
2829

2930
<!-- Android -->

0 commit comments

Comments
 (0)