I have some code for picking a file on Android. I picked it from the web somewhere. Actually, the code works in one scenario, but in my app, it doesn't work. Android thinks that the user has cancelled the file pick when they actually haven't.
Here's the code:
private Task<FileData> PickAndOpenFile(bool isSave)
{
var id = GetRequestId();
var ntcs = new TaskCompletionSource<FileData>(id);
if (Interlocked.CompareExchange(ref _CompletionSource, ntcs, null) != null)
{
throw new InvalidOperationException("Only one operation can be active at a time");
}
try
{
var pickerIntent = new Intent(_Context, typeof(FilePickerActivity));
pickerIntent.SetFlags(ActivityFlags.NewTask);
_Context.StartActivity(pickerIntent);
EventHandler<FilePickerEventArgs> handler = null;
EventHandler<EventArgs> cancelledHandler = null;
handler = (s, e) =>
{
var tcs = Interlocked.Exchange(ref _CompletionSource, null);
FilePickerActivity.FilePicked -= handler;
Stream fileStream = isSave ? File.OpenRead(e.FilePath) : File.OpenWrite(e.FilePath);
tcs?.SetResult(new FileData { FileName = e.FileName, FileStream = fileStream });
};
cancelledHandler = (s, e) =>
{
var tcs = Interlocked.Exchange(ref _CompletionSource, null);
FilePickerActivity.FilePickCancelled -= cancelledHandler;
tcs?.SetResult(null);
};
FilePickerActivity.FilePickCancelled += cancelledHandler;
FilePickerActivity.FilePicked += handler;
}
catch (Exception exAct)
{
Debug.Write(exAct);
}
return _CompletionSource.Task;
}
To see this running correctly, please clone this repo and run the android sample. The choose file example is on the second tab. https://github.com/MelbourneDeveloper/Adapt.Presentation.git
But, in my app, I have exactly the same code used like this:
var filePicker = App.PlatformSpecificStartupArguments.PresentationFactory.CreateFilePicker();
using (var fileData = await filePicker.PickAndOpenFileForReading())
{
if (fileData == null)
{
return;
}
using (var readFileStream = fileData.FileStream)
{
var fileExtension = Path.GetExtension(fileData.FileName);
var relativeUri = Guid.NewGuid() + fileExtension;
await OnPictureTakenAsync(this, new PictureTakenEventArgs(readFileStream, relativeUri, fileData.FileName));
}
}
But, when I pick the file (selecting an image from existing images), the code enters the cancelledHandler instead. This is clearly a permissions problem because I get this error message in the device log:
08-02 15:15:28.697 LGE Nexus 5X Error 18911 DatabaseUtils java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/images/media from pid=18684, uid=10221 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission() at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:608) at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:483) at android.content.ContentProvider$Transport.query(ContentProvider.java:212) at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:112) at android.os.Binder.execTransact(Binder.java:565)
So, I turned on this permission in my Android Manifest at the Visual Studio level, but the problem still persists. The version of Android that I am targeting where this is failing is 6.0. You can see that in the repo mentioned above the target version is set to "Use compile using SDK version". And I haven't even added READ_EXTERNAL_STORAGE to that sample app.