Skip to content
This repository was archived by the owner on Jan 14, 2021. It is now read-only.

Commit 6d6714b

Browse files
vividosjfversluis
authored andcommitted
Merge to master (#102)
* Added support for file types across platforms (#26) * Added support for file types across platforms - Added per platform file type support, so you can specify what kind of files you want the picker to support - Updated iOS's picker to use the non-deprecated `UIDocumentPickerViewController` - Minor tweaks, such as package updates * Fixed an issue with the iOS implementation, where the returned path wasn't properly formatted/absolute, which in turn meant that the path wasn't valid as is. * A few iOS specific improvements * Get latest changes to master (#51) * pull master into develop before merging back (#47) * Resolved issues with referencing from NuGet package (#29) Simplified the nuspec file to work with wildcards and fixed macOS assembly name. Also added development NuGet feed to readme along with some other tweaks * Project fixes (#35) * fixed paths to NuGet packages folder; always relative to the .sln file * converted samples solution file to VS 2017 and fixed "Deploy" flag for Android app * ignore Android Resource designer generated files # Conflicts: # .gitignore * changes automatically applied by VS 2017 when opening iOS project file * Added issue template * fixes crash in Xamarin.Forms sample when user doesn't select file; also added example code in README file (#50) * fixed picking files from the download folder; on newer devices the document ID may not be a number, but the real filename prefixed with "raw:" (#49) * Updated dependencies * Revert "Fixed Path" (#65) * Revert "Fixed Path (#54)" * when opening files with the sample app, ignore if filename extension is uppercase or lowercase * removed unused method DidPickDocumentPicker() in iOS implementation * added troubleshooting chapter to readme with common errors and their solutions * updated sample projects to use PackageReference to reference NuGet packages * removed obsolete project property AndroidUseLatestPlatformSdk and set target framework to Android 9.0 (API 28) * updated Plugin.FilePicker.UWP to use PackageReference * removed obsolete property AndroidUseLatestPlatformSdk and target Android 9.0, in Plugin.FilePicker.Android project * updated sample projects to use Xamarin.Forms 3.3.0 NuGet package * updated UWP project to latest stable UWP NuGet package * fixed Android sample project by re-adding Xamarin.Forms NuGet package * added UWP sample project * find correct view controller to present document picker, by finding currently presented view controller * Android: return from task with Exception that was thrown during file picking before that, the caller only got a null FileData object without knowing what happened * Android: also pass exception to task when setting up picking * iOS: pass exceptions in DocumentPicker handler to picker task * renamed Forms project folder to a shorter name and renamed the only page to MainPage * added try-catch to call to PickFile() in order to display exceptions from picking * delete Resource.designer.cs file from repository that is being regenerated by the compiler * converted Xamarin.Forms sample project to use Project Sdk style .csproj proect file * added second button to samples to pick image files * added check if Android permission was granted * fix android mime types implementation (#85) (cherry picked from commit 04a5292) * added explanation for requesting permissions on Android (#89) * fixed exception when trying to pick a downloaded file; the Android DownloadManager won't return an actual file path; downloaded files can only be resolved using ContentResolver (#86) * added solution folders to organize projects * added UTType.Image file types value for picking on iOS * Forms sample project can use .NET Standard 1.0; no need for version 2.0, which might not be installed * added README.md for samples folder * added Plugin.Permissions NuGet package to sample project, in order to check for permissions * added checking Storage permission when on Android device * iOS: simplified getting filename from pathname * iOS: fixed getting pathname from FileUrl (fix of PR #54 that was reverted in PR #65); tested on iOS 11 device * added documentation about FileData class to README * Fix FileName for Mac FileName on Mac returned the path instead. * Fix OpenFile for Mac * fixed getting path from OneDrive (#79) the content provider only returned a relative filename in the data column, so it's better to use the content:// uri to access the file * fixed indentation of release notes text in .nuspec - it should look nice on the nuget.org page now * Android: file bytes are not read directly after picking, but only when FileData.DataArray is accessed or FileData.GetStream() is called; this fixes accessing large files that wouldn't fit in the device's memory, e.g. videos (#38) * iOS: also removed getting data bytes when it's not used in FilePickerEventArgs * added a troubleshooting section to explain why picked files on iOS may be gone after some time (#87, #74) * added SourceLink integration, see https://github.com/dotnet/sourcelink * added documentation for method PickFile(), in order to document how to use allowedTypes (#59) * Revert "added SourceLink integration, see https://github.com/dotnet/sourcelink" This reverts commit 1ca5796. * documented classes in Plugin.FilePicker.Abstractions and fixed line endings
1 parent ce173b2 commit 6d6714b

File tree

10 files changed

+191
-90
lines changed

10 files changed

+191
-90
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,23 @@ Call **CrossFilePicker.Current** from any platform or .NET Standard project to g
5555
System.Console.WriteLine("Exception choosing file: " + ex.ToString());
5656
}
5757

58+
### Methods
59+
60+
#### `async Task<FileData> PickFile(string[] allowedTypes = null)`
61+
62+
Starts file picking and returns file data for picked file. File types can be
63+
specified in order to limit files that can be selected. Note that this method
64+
may throw exceptions that occured during file picking.
65+
66+
Parameter `allowedTypes`:
67+
Specifies one or multiple allowed types. When null, all file types can be
68+
selected while picking.
69+
70+
On Android you can specify one or more MIME types, e.g. "image/png"; also wild
71+
card characters can be used, e.g. "image/*".
72+
73+
On iOS you can specify UTType constants, e.g. UTType.Image.
74+
5875
### Data structures
5976

6077
The returned `FileData` object contains multiple properties that can be accessed:
@@ -119,6 +136,17 @@ before) but the permission must be requested and granted by the user at runtime
119136
the user denied the permission, don't call PickFile(). Check out the sample project in the github
120137
repository for an example how to check for permission.
121138

139+
### iOS
140+
141+
**Picked file path is invalid, file doesn't exist**
142+
143+
On iOS the plugin uses UIDocumentPickerViewController and specifies the mode
144+
UIDocumentPickerMode.Import. After picking is done, iOS copies the picked file
145+
to the app's "Inbox" folder where it can be accessed. iOS also cleans up the
146+
temporary inbox folder regularly. After picking the file you have to either
147+
copy the file to another folder or access the data by getting the property
148+
DataBytes or opening a stream to the file by calling GetStream().
149+
122150
## Contributors
123151
* [jfversluis](https://github.com/jfversluis)
124152
* [rafaelrmou](https://github.com/rafaelrmou) (original author)

nuget/Plugin.FilePicker.nuspec

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,12 @@
1515
</description>
1616
<summary>Simple cross-platform plug-in that allows you to pick files and work with them. View full project page for README
1717
</summary>
18-
<releaseNotes>
19-
[2.0] - Support for .NET Standard, removed Windows Phone 8 and Windows 8/8.1 support
20-
[1.4] - Support Xamarin.Mac
21-
[1.3] - FileData now has a GetStream() method to retrieve the binary data as a Stream
22-
[1.2] - Retrieve full file path from selected file
23-
[1.1] - Support Windows Phone 8, 8.1 e Windows Store.
24-
[1.0] - Pick files, save files and open the archive in Default App.
18+
<releaseNotes>[2.0] - Support for .NET Standard, removed Windows Phone 8 and Windows 8/8.1 support
19+
[1.4] - Support Xamarin.Mac
20+
[1.3] - FileData now has a GetStream() method to retrieve the binary data as a Stream
21+
[1.2] - Retrieve full file path from selected file
22+
[1.1] - Support Windows Phone 8, 8.1 e Windows Store.
23+
[1.0] - Pick files, save files and open the archive in Default App.
2524
</releaseNotes>
2625
<tags>xamarin, file, picker, filepicker, filebrowser, netstandard, plugin, plugin for xamarin, android, xamarin.forms, ios</tags>
2726
</metadata>

src/Plugin.FilePicker/Plugin.FilePicker.Abstractions/FileData.cs

Lines changed: 71 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,57 @@
44
namespace Plugin.FilePicker.Abstractions
55
{
66
/// <summary>
7-
/// The object used as a wrapper for the file picked by the user
7+
/// File data that specifies a file that was picked by the user.
88
/// </summary>
99
public class FileData : IDisposable
1010
{
11+
/// <summary>
12+
/// Backing store for the FileName property
13+
/// </summary>
1114
private string _fileName;
15+
16+
/// <summary>
17+
/// Backing store for the FilePath property
18+
/// </summary>
1219
private string _filePath;
20+
21+
/// <summary>
22+
/// Indicates if the object is already disposed
23+
/// </summary>
1324
private bool _isDisposed;
25+
26+
/// <summary>
27+
/// Action to dispose of underlying resources of the picked file.
28+
/// </summary>
1429
private readonly Action<bool> _dispose;
30+
31+
/// <summary>
32+
/// Function to get a stream to the picked file.
33+
/// </summary>
1534
private readonly Func<Stream> _streamGetter;
1635

36+
/// <summary>
37+
/// Creates a new and empty file data object
38+
/// </summary>
1739
public FileData()
18-
{ }
40+
{
41+
}
1942

43+
/// <summary>
44+
/// Creates a new file data object with property values
45+
/// </summary>
46+
/// <param name="filePath">
47+
/// Full file path to the picked file.
48+
/// </param>
49+
/// <param name="fileName">
50+
/// File name of the picked file.
51+
/// </param>
52+
/// <param name="streamGetter">
53+
/// Function to get a stream to the picked file.
54+
/// </param>
55+
/// <param name="dispose">
56+
/// Action to dispose of the underlying resources of the picked file.
57+
/// </param>
2058
public FileData(string filePath, string fileName, Func<Stream> streamGetter, Action<bool> dispose = null)
2159
{
2260
_filePath = filePath;
@@ -40,6 +78,12 @@ public static byte[] ReadFully(Stream input)
4078
}
4179
}
4280

81+
/// <summary>
82+
/// Returns the raw data bytes for the picked file.
83+
/// Note that due to how this property is implemented, you may only
84+
/// call this once. You can always access the underlying stream by
85+
/// directly calling GetStream().
86+
/// </summary>
4387
public byte[] DataArray
4488
{
4589
get
@@ -52,7 +96,7 @@ public byte[] DataArray
5296
}
5397

5498
/// <summary>
55-
/// Filename of the picked file
99+
/// Filename of the picked file, without path
56100
/// </summary>
57101
public string FileName
58102
{
@@ -74,7 +118,10 @@ public string FileName
74118
}
75119

76120
/// <summary>
77-
/// Full filepath of the picked file
121+
/// Full filepath of the picked file.
122+
/// Note that on specific platforms this can also contain an URI that
123+
/// can't be opened with file related APIs. Use DataArray property or
124+
/// GetStream() method in this cases.
78125
/// </summary>
79126
public string FilePath
80127
{
@@ -96,7 +143,9 @@ public string FilePath
96143
}
97144

98145
/// <summary>
99-
/// Get stream if available
146+
/// Gets stream to access the picked file.
147+
/// Note that when DataArray property was already accessed, the stream
148+
/// must be rewinded to the beginning.
100149
/// </summary>
101150
/// <returns>stream object</returns>
102151
public Stream GetStream()
@@ -107,24 +156,40 @@ public Stream GetStream()
107156
return _streamGetter();
108157
}
109158

159+
#region IDispose implementation
160+
/// <summary>
161+
/// Disposes of all resources in the object
162+
/// </summary>
110163
public void Dispose()
111164
{
112165
Dispose(true);
113166
GC.SuppressFinalize(this);
114167
}
115168

169+
/// <summary>
170+
/// Disposes of managed resources
171+
/// </summary>
172+
/// <param name="disposing">
173+
/// True when called from Dispose(), false when called from the destructor
174+
/// </param>
116175
private void Dispose(bool disposing)
117176
{
118177
if (_isDisposed)
178+
{
119179
return;
180+
}
120181

121182
_isDisposed = true;
122183
_dispose?.Invoke(disposing);
123184
}
124185

186+
/// <summary>
187+
/// Finalizer for this object
188+
/// </summary>
125189
~FileData()
126190
{
127191
Dispose(false);
128192
}
193+
#endregion
129194
}
130-
}
195+
}

src/Plugin.FilePicker/Plugin.FilePicker.Abstractions/FilePickerEventArgs.cs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,36 @@
22

33
namespace Plugin.FilePicker.Abstractions
44
{
5+
/// <summary>
6+
/// Event arguments for the event when file picking was completed.
7+
/// </summary>
58
public class FilePickerEventArgs : EventArgs
69
{
7-
public byte [] FileByte { get; set; }
10+
public byte[] FileByte { get; set; }
811

912
public string FileName { get; set; }
1013

1114
public string FilePath { get; set; }
1215

13-
public FilePickerEventArgs ()
16+
public FilePickerEventArgs()
1417
{
15-
1618
}
1719

18-
public FilePickerEventArgs (byte [] fileByte)
20+
public FilePickerEventArgs(byte[] fileByte)
1921
{
2022
FileByte = fileByte;
2123
}
2224

23-
public FilePickerEventArgs (byte [] fileByte, string fileName)
24-
: this (fileByte)
25+
public FilePickerEventArgs(byte[] fileByte, string fileName)
26+
: this(fileByte)
2527
{
2628
FileName = fileName;
2729
}
2830

29-
public FilePickerEventArgs (byte [] fileByte, string fileName, string filePath)
30-
: this (fileByte, fileName)
31+
public FilePickerEventArgs(byte[] fileByte, string fileName, string filePath)
32+
: this(fileByte, fileName)
3133
{
3234
FilePath = filePath;
3335
}
3436
}
35-
}
37+
}
Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,61 @@
11
using System.Threading.Tasks;
22

33
namespace Plugin.FilePicker.Abstractions
4-
{
4+
{
55
/// <summary>
6-
/// Interface for FilePicker
6+
/// Interface for FilePicker plugin. Access the platform specific instance
7+
/// of this interface by using CrossFilePicker.Current.
78
/// </summary>
8-
public interface IFilePicker
9-
{
10-
Task<FileData> PickFile (string[] allowedTypes = null);
11-
12-
Task<bool> SaveFile (FileData fileToSave);
13-
14-
void OpenFile (string fileToOpen);
15-
16-
void OpenFile (FileData fileToOpen);
9+
public interface IFilePicker
10+
{
11+
/// <summary>
12+
/// Starts file picking and returns file data for picked file. File
13+
/// types can be specified in order to limit files that can be
14+
/// selected. Note that this method may throw exceptions that occured
15+
/// during file picking.
16+
/// </summary>
17+
/// <param name="allowedTypes">
18+
/// Specifies one or multiple allowed types. When null, all file types
19+
/// can be selected while picking.
20+
/// On Android you can specify one or more MIME types, e.g.
21+
/// "image/png"; also wild card characters can be used, e.g. "image/*".
22+
/// On iOS you can specify UTType constants, e.g. UTType.Image.
23+
/// </param>
24+
/// <returns>
25+
/// File data object, or null when user cancelled picking file
26+
/// </returns>
27+
Task<FileData> PickFile(string[] allowedTypes = null);
28+
29+
/// <summary>
30+
/// Saves the file that was picked to external storage.
31+
/// </summary>
32+
/// <param name="fileToSave">
33+
/// File data from a call to PickFile() that should be saved.
34+
/// </param>
35+
/// <returns>
36+
/// True when file was saved successfully, false when there was an
37+
/// error
38+
/// </returns>
39+
Task<bool> SaveFile(FileData fileToSave);
40+
41+
/// <summary>
42+
/// Opens the file with given filename in an external application that
43+
/// is registered for this file type.
44+
/// </summary>
45+
/// <param name="fileToOpen">
46+
/// Full filename of the file to open
47+
/// </param>
48+
void OpenFile(string fileToOpen);
49+
50+
/// <summary>
51+
/// Opens the file specified by first storing the file to external
52+
/// storage, then opening it in an external application that is
53+
/// registered for this file type. This is a combination of SaveFile()
54+
/// and OpenFile() above.
55+
/// </summary>
56+
/// <param name="fileToOpen">
57+
/// File data from a call to PickFile() that should be opened.
58+
/// </param>
59+
void OpenFile(FileData fileToOpen);
1760
}
18-
}
61+
}

src/Plugin.FilePicker/Plugin.FilePicker.Android/FilePickerActivity.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,10 @@ protected override void OnActivityResult (int requestCode, [GeneratedEnum] Resul
5959

6060
if (string.IsNullOrEmpty (filePath))
6161
filePath = IOUtil.isMediaStore(_uri.Scheme) ? _uri.ToString() : _uri.Path;
62-
byte[] file;
63-
if (IOUtil.isMediaStore(_uri.Scheme))
64-
file = IOUtil.readFile(context, _uri);
65-
else
66-
file = IOUtil.readFile (filePath);
6762

6863
var fileName = GetFileName (context, _uri);
6964

70-
OnFilePicked (new FilePickerEventArgs (file, fileName, filePath));
65+
OnFilePicked (new FilePickerEventArgs (null, fileName, filePath));
7166
} catch (Exception readEx) {
7267
System.Diagnostics.Debug.Write(readEx);
7368
// Notify user file picking failed.

src/Plugin.FilePicker/Plugin.FilePicker.Android/FilePickerImplementation.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ private Task<FileData> TakeMediaAsync(string[] allowedTypes, string action)
7272
() =>
7373
{
7474
if (IOUtil.isMediaStore(e.FilePath))
75-
return new System.IO.MemoryStream(e.FileByte);
75+
{
76+
var contentUri = Android.Net.Uri.Parse(e.FilePath);
77+
return Application.Context.ContentResolver.OpenInputStream(contentUri);
78+
}
7679
else
7780
return System.IO.File.OpenRead (e.FilePath);
7881
}));

0 commit comments

Comments
 (0)