Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/filesavepicker #3380

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/SamplesApp/UITests.Shared/UITests.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_Storage\FilePickers\FileSavePickerTest.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="$(MSBuildThisFileDirectory)Windows_Storage\StorageFolderTests\Persistence.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
Expand Down Expand Up @@ -3334,6 +3338,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Windows_Phone\Devices_Notifications_VibrationDevice.xaml.cs">
<DependentUpon>Devices_Notifications_VibrationDevice.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_Storage\FilePickers\FileSavePickerTest.xaml.cs">
<DependentUpon>FileSavePickerTest.xaml</DependentUpon>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Windows_Storage\StorageFolderTests\Persistence.xaml.cs">
<DependentUpon>Persistence.xaml</DependentUpon>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<Page
x:Class="UITests.Shared.Windows_Storage.FilePickers.FileSavePickerTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

<StackPanel>
<Button Content="Save pdf" Click="SavePdfButton_Click" Margin="0,10,10,0"/>
<Button Content="Save image" Click="SaveImgButton_Click" Margin="0,10,10,0"/>
<Button Content="Save txt" Click="SaveTxtButton_Click" Margin="0,10,10,0"/>

<TextBlock Text="File name:"/>
<TextBlock x:Name="FileName"/>

<TextBlock Text="File path:" />
<TextBlock x:Name="FilePath"/>

<TextBlock Text="File Status:" />
<TextBlock x:Name="FileUpdateStatus"/>

<TextBlock Text="Action output:"/>
<TextBlock x:Name="OutputTextBlock"/>

</StackPanel>

</Page>

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/Uno.UI.Wasm/Uno.UI.Wasm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
<UpToDateCheckInput Include="tsBindings\**\*" />
</ItemGroup>

<ItemGroup>
<UpToDateCheckInput Remove="ts\Windows\Storage\FileSavePicker.ts" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="LinkerDefinition.xml">
<LogicalName>$(AssemblyName).xml</LogicalName>
Expand Down
5 changes: 5 additions & 0 deletions src/Uno.UI.Wasm/WasmScripts/Uno.UI.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,6 +917,11 @@ declare namespace Windows.Storage {
private static getValueByIndex;
}
}
declare namespace Windows.Storage.Pickers {
class FileSavePicker {
static SaveAs(fileName: string, dataPtr: any, size: number): void;
}
}
declare namespace Windows.Storage {
class StorageFolder {
private static _isInit;
Expand Down
25 changes: 25 additions & 0 deletions src/Uno.UI.Wasm/WasmScripts/Uno.UI.js
Original file line number Diff line number Diff line change
Expand Up @@ -2917,6 +2917,31 @@ var Windows;
})(Storage = Windows.Storage || (Windows.Storage = {}));
})(Windows || (Windows = {}));
var Windows;
(function (Windows) {
var Storage;
(function (Storage) {
var Pickers;
(function (Pickers) {
class FileSavePicker {
static SaveAs(fileName, dataPtr, size) {
var buffer = new Uint8Array(size);
for (var i = 0; i < size; i++) {
buffer[i] = Module.getValue(dataPtr + i, "i8");
}
var a = window.document.createElement('a');
var blob = new Blob([buffer]);
a.href = window.URL.createObjectURL(blob);
a.download = fileName;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
Pickers.FileSavePicker = FileSavePicker;
})(Pickers = Storage.Pickers || (Storage.Pickers = {}));
})(Storage = Windows.Storage || (Windows.Storage = {}));
})(Windows || (Windows = {}));
var Windows;
(function (Windows) {
var Storage;
(function (Storage) {
Expand Down
23 changes: 23 additions & 0 deletions src/Uno.UI.Wasm/ts/Windows/Storage/FileSavePicker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Windows.Storage.Pickers {

export class FileSavePicker {
public static SaveAs(fileName: string, dataPtr: any, size: number): void {

var buffer = new Uint8Array(size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


for (var i = 0; i < size; i++) {
buffer[i] = Module.getValue(dataPtr + i, "i8");
}

var a = window.document.createElement('a');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

var blob = new Blob([buffer]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


a.href = window.URL.createObjectURL(blob);
a.download = fileName;

document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
}
28 changes: 28 additions & 0 deletions src/Uno.UWP/ActivityResultArgs.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#if __ANDROID__
using Android.App;
using Android.Content;

namespace Uno
{
/// <summary>
/// OnActivityResultArgs : an object containing the parameters of the Activity.OnActivityResult
/// </summary>
/// <param name="ct">CancellationToken</param>
/// <param name="intent">The Intent you want to send</param>
/// <param name="requestCode">A specific Response code, this is useful if you send more than one type of request in parallel</param>
public class ActivityResultArgs
{
public ActivityResultArgs(int requestCode, Result resultCode, Intent intent)
{
RequestCode = requestCode;
ResultCode = resultCode;
Intent = intent;
}
public Intent Intent { get; }

public int RequestCode { get; }

public Result ResultCode { get; }
}
}
#endif
116 changes: 116 additions & 0 deletions src/Uno.UWP/AsyncActivity.Android.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
#if __ANDROID__

using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Uno.UI;

namespace Uno
{

[Activity(ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class AsyncActivity : Activity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to be internal

{
// Some devices (Galaxy S4) use a second activity to get the result.
// This dictionary is used to keep track of the original activities that requests the values.
private static readonly Dictionary<int, AsyncActivity> _originalActivities = new Dictionary<int, AsyncActivity>();
private readonly TaskCompletionSource<OnActivityResultArgs> _completionSource = new TaskCompletionSource<OnActivityResultArgs>();
private static event Action<AsyncActivity> Handler;

public int RequestCode { get; private set; }

public AsyncActivity() { }

protected override void OnResume()
{
base.OnResume();

Handler?.Invoke(this);
}

public static async Task<T> InitialiseActivity<T>(Intent intent, int requestCode = 0) where T : AsyncActivity
{
var finished = new TaskCompletionSource<AsyncActivity>();

var taskCompletionSource = new TaskCompletionSource<AsyncActivity>();
void handler(AsyncActivity instance) => taskCompletionSource.TrySetResult(instance);

try
{
Handler += handler;
ContextHelper.Current.StartActivity(typeof(T));

var result = await taskCompletionSource.Task as T;
result.RequestCode = requestCode;
result.Intent = intent;
return result;
}
finally
{
Handler -= handler;
}
}

public async Task<OnActivityResultArgs> Start()
{
try
{
_originalActivities[RequestCode] = this;

StartActivityForResult(Intent, RequestCode);

//the Task that returns when OnActivityResult is called
var result = await _completionSource.Task;
return result;
}
finally
{
Finish();//Close the activity
_originalActivities.Remove(RequestCode);
}
}

protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
base.OnActivityResult(requestCode, resultCode, intent);

// Some devices (Galaxy S4) use a second activity to get the result.
// In this case the current instance is not the same as the one that requested the value.
// In this case we must get the original activity and use it's TaskCompletionSource instead of ours.
var currentActivityIsANewOne = false;
if (_originalActivities.TryGetValue(requestCode, out var originalActivity))
{
currentActivityIsANewOne = originalActivity != this;
}
else
{
originalActivity = this;
}

if (currentActivityIsANewOne)
{
// Finish this activity (because we are not the original)
Finish();
}

// Push a new OnActivityResultArgs in the calling activity
if (originalActivity.RequestCode == requestCode)
{
originalActivity._completionSource.TrySetResult(new OnActivityResultArgs(requestCode, resultCode, intent));
}
}
protected override void OnDestroy()
{
base.OnDestroy();

//AsyncActivity could be destroyed by the system before receiving the result,
//In such a case, we need to complete the _completionSource. In the normal flow, OnDestroy will
//only be called after the _completionSource's Task has already RanToCompletion
_completionSource?.TrySetResult(new OnActivityResultArgs(RequestCode, Result.Canceled, null));
}
}
}
#endif
Loading