Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

Commit

Permalink
Rework the "picker" results to correctly manage files (#1555)
Browse files Browse the repository at this point in the history
* Rework the "picker" results to correctly manage lifetime of files and URIs
* Extract magic strings into constants
   - Mime types
   - Extensions
   - Changed property type of ShareMultipleFilesRequest.Files to be a List<T> for consistency
  • Loading branch information
mattleibow authored Dec 2, 2020
1 parent ba91194 commit add928f
Show file tree
Hide file tree
Showing 20 changed files with 572 additions and 243 deletions.
9 changes: 9 additions & 0 deletions Samples/Samples.Android/Properties/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,35 @@
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<queries>
<!-- Email -->
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="http" />
</intent>
<!-- Browser -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
<!-- Sms -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="smsto" />
</intent>
<!-- PhoneDialer -->
<intent>
<action android:name="android.intent.action.DIAL" />
<data android:scheme="tel" />
</intent>
<!-- MediaPicker -->
<intent>
<action android:name="android.media.action.IMAGE_CAPTURE" />
</intent>
</queries>
<uses-feature android:name="android.hardware.location" android:required="false" />
<uses-feature android:name="android.hardware.location.gps" android:required="false" />
Expand Down
5 changes: 3 additions & 2 deletions Samples/Samples/ViewModel/ShareViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.IO;
using System.Collections.Generic;
using System.IO;
using System.Windows.Input;
using Samples.Helpers;
using Xamarin.Essentials;
Expand Down Expand Up @@ -157,7 +158,7 @@ async void OnFilesRequest(Xamarin.Forms.View element)
await Share.RequestAsync(new ShareMultipleFilesRequest
{
Title = ShareFilesTitle,
Files = new ShareFile[] { new ShareFile(file1), new ShareFile(file2) },
Files = new List<ShareFile> { new ShareFile(file1), new ShareFile(file2) },
PresentationSourceBounds = GetRectangle(element)
});
}
Expand Down
21 changes: 21 additions & 0 deletions Tests/FileSystem_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,26 @@ public async Task OpenAppPackageFileAsync_Fail_On_NetStandard()
{
await Assert.ThrowsAsync<NotImplementedInReferenceAssemblyException>(() => FileSystem.OpenAppPackageFileAsync("filename.txt"));
}

[Theory]
[InlineData(null, "")]
[InlineData("", "")]
[InlineData(".", ".")]
[InlineData(".txt", ".txt")]
[InlineData("*.txt", ".txt")]
[InlineData("*.*", ".*")]
[InlineData("txt", ".txt")]
[InlineData("test.txt", ".test.txt")]
[InlineData("test.", ".test.")]
[InlineData("....txt", ".txt")]
[InlineData("******txt", ".txt")]
[InlineData("******.txt", ".txt")]
[InlineData("******.......txt", ".txt")]
public void Extensions_Clean_Correctly_Cleans_Extensions(string input, string output)
{
var cleaned = FileSystem.Extensions.Clean(input);

Assert.Equal(output, cleaned);
}
}
}
2 changes: 1 addition & 1 deletion Xamarin.Essentials/Email/Email.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ static Intent CreateIntent(EmailMessage message)
if (action == Intent.ActionSendto)
intent.SetData(Uri.Parse("mailto:"));
else
intent.SetType("message/rfc822");
intent.SetType(FileSystem.MimeTypes.EmailMessage);

if (!string.IsNullOrEmpty(message?.Body))
{
Expand Down
48 changes: 26 additions & 22 deletions Xamarin.Essentials/FilePicker/FilePicker.android.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.Provider;

namespace Xamarin.Essentials
{
Expand All @@ -22,7 +19,7 @@ static async Task<IEnumerable<FileResult>> PlatformPickAsync(PickOptions options
var action = Intent.ActionOpenDocument;

var intent = new Intent(action);
intent.SetType("*/*");
intent.SetType(FileSystem.MimeTypes.All);
intent.PutExtra(Intent.ExtraAllowMultiple, allowMultiple);

var allowedTypes = options?.FileTypes?.Value?.ToArray();
Expand All @@ -33,23 +30,30 @@ static async Task<IEnumerable<FileResult>> PlatformPickAsync(PickOptions options

try
{
var result = await IntermediateActivity.StartAsync(pickerIntent, Platform.requestCodeFilePicker);
var resultList = new List<FileResult>();

var clipData = new List<global::Android.Net.Uri>();

if (result.ClipData == null)
void OnResult(Intent intent)
{
clipData.Add(result.Data);
}
else
{
for (var i = 0; i < result.ClipData.ItemCount; i++)
clipData.Add(result.ClipData.GetItemAt(i).Uri);
// The uri returned is only temporary and only lives as long as the Activity that requested it,
// so this means that it will always be cleaned up by the time we need it because we are using
// an intermediate activity.

if (intent.ClipData == null)
{
var path = FileSystem.EnsurePhysicalPath(intent.Data);
resultList.Add(new FileResult(path));
}
else
{
for (var i = 0; i < intent.ClipData.ItemCount; i++)
{
var uri = intent.ClipData.GetItemAt(i).Uri;
var path = FileSystem.EnsurePhysicalPath(uri);
resultList.Add(new FileResult(path));
}
}
}

foreach (var contentUri in clipData)
resultList.Add(new FileResult(contentUri));
await IntermediateActivity.StartAsync(pickerIntent, Platform.requestCodeFilePicker, onResult: OnResult);

return resultList;
}
Expand All @@ -65,31 +69,31 @@ public partial class FilePickerFileType
static FilePickerFileType PlatformImageFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "image/png", "image/jpeg" } }
{ DevicePlatform.Android, new[] { FileSystem.MimeTypes.ImagePng, FileSystem.MimeTypes.ImageJpg } }
});

static FilePickerFileType PlatformPngFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "image/png" } }
{ DevicePlatform.Android, new[] { FileSystem.MimeTypes.ImagePng } }
});

static FilePickerFileType PlatformJpegFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "image/jpeg" } }
{ DevicePlatform.Android, new[] { FileSystem.MimeTypes.ImageJpg } }
});

static FilePickerFileType PlatformVideoFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "video/*" } }
{ DevicePlatform.Android, new[] { FileSystem.MimeTypes.VideoAll } }
});

static FilePickerFileType PlatformPdfFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Android, new[] { "application/pdf" } }
{ DevicePlatform.Android, new[] { FileSystem.MimeTypes.Pdf } }
});
}
}
12 changes: 4 additions & 8 deletions Xamarin.Essentials/FilePicker/FilePicker.ios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ static Task<IEnumerable<FileResult>> PlatformPickAsync(PickOptions options, bool
{
try
{
// there was a cancellation
tcs.TrySetResult(GetFileResults(urls));
}
catch (Exception ex)
Expand Down Expand Up @@ -72,13 +71,10 @@ static Task<IEnumerable<FileResult>> PlatformPickAsync(PickOptions options, bool
return tcs.Task;
}

static IEnumerable<FileResult> GetFileResults(NSUrl[] urls)
{
if (urls?.Length > 0)
return urls.Select(url => new UIDocumentFileResult(url));
else
return Enumerable.Empty<FileResult>();
}
static IEnumerable<FileResult> GetFileResults(NSUrl[] urls) =>
urls?.Length > 0
? urls.Select(url => new UIDocumentFileResult(url))
: Enumerable.Empty<FileResult>();

class PickerDelegate : UIDocumentPickerDelegate
{
Expand Down
12 changes: 6 additions & 6 deletions Xamarin.Essentials/FilePicker/FilePicker.tizen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static async Task<IEnumerable<FileResult>> PlatformPickAsync(PickOptions options
appControl.LaunchMode = AppControlLaunchMode.Single;

var fileType = options?.FileTypes?.Value?.FirstOrDefault();
appControl.Mime = fileType ?? "*/*";
appControl.Mime = fileType ?? FileSystem.MimeTypes.All;

var fileResults = new List<FileResult>();

Expand All @@ -51,31 +51,31 @@ public partial class FilePickerFileType
static FilePickerFileType PlatformImageFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Tizen, new[] { "image/*" } },
{ DevicePlatform.Tizen, new[] { FileSystem.MimeTypes.ImageAll } },
});

static FilePickerFileType PlatformPngFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Tizen, new[] { "image/png" } }
{ DevicePlatform.Tizen, new[] { FileSystem.MimeTypes.ImagePng } }
});

static FilePickerFileType PlatformJpegFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Tizen, new[] { "image/jpeg" } }
{ DevicePlatform.Tizen, new[] { FileSystem.MimeTypes.ImageJpg } }
});

static FilePickerFileType PlatformVideoFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Tizen, new[] { "video/*" } }
{ DevicePlatform.Tizen, new[] { FileSystem.MimeTypes.VideoAll } }
});

static FilePickerFileType PlatformPdfFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.Tizen, new[] { "application/pdf" } }
{ DevicePlatform.Tizen, new[] { FileSystem.MimeTypes.Pdf } }
});
}
}
15 changes: 8 additions & 7 deletions Xamarin.Essentials/FilePicker/FilePicker.uwp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,10 @@ static void SetFileTypes(PickOptions options, FileOpenPicker picker)
{
foreach (var type in options.FileTypes.Value)
{
if (type.StartsWith(".") || type.StartsWith("*."))
var ext = FileSystem.Extensions.Clean(type);
if (!string.IsNullOrWhiteSpace(ext))
{
picker.FileTypeFilter.Add(type.TrimStart('*'));
picker.FileTypeFilter.Add(ext);
hasAtLeastOneType = true;
}
}
Expand All @@ -67,31 +68,31 @@ public partial class FilePickerFileType
static FilePickerFileType PlatformImageFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.UWP, new[] { "*.png", "*.jpg", "*.jpeg", "*.gif", "*.bmp" } }
{ DevicePlatform.UWP, FileSystem.Extensions.AllImage }
});

static FilePickerFileType PlatformPngFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.UWP, new[] { "*.png" } }
{ DevicePlatform.UWP, new[] { FileSystem.Extensions.Png } }
});

static FilePickerFileType PlatformJpegFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.UWP, new[] { "*.jpg", "*.jpeg" } }
{ DevicePlatform.UWP, FileSystem.Extensions.AllJpeg }
});

static FilePickerFileType PlatformVideoFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.UWP, new[] { "*.mp4", "*.mov", "*.avi", "*.wmv", "*.m4v", "*.mpg", "*.mpeg", "*.mp2", "*.mkv", "*.flv", "*.gifv", "*.qt" } }
{ DevicePlatform.UWP, FileSystem.Extensions.AllVideo }
});

static FilePickerFileType PlatformPdfFileType() =>
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{ DevicePlatform.UWP, new[] { "*.pdf" } }
{ DevicePlatform.UWP, new[] { FileSystem.Extensions.Pdf } }
});
}
}
Loading

0 comments on commit add928f

Please sign in to comment.