Closed ChristianKleineidam closed 5 years ago
Thanks for the bug report. It might be that you can store the task that is returned by PickFile() and try to cancel it when you re-enter your picking method and trying to call PickFile() again. Did you already try to apply some fix? Alternatively you could try to fix the FilePicker code and send us a PR. I don't know exactly where you could recognize that the file picker is cancelled, maybe in a OnDestroy() handler.
At the moment I simply tell the user to restart the app.
I'm not sure why this error is needed in the first place. Why can't the function do the same thing it would do when it would be called normally?
This is something we picked up from the "legacy" code. Not sure why it happens, might be good to investigate but I'm sure there was a good reason at the time :)
I debugged this issue and I can reproduce it. When calling PickFile(), a temporary activity (FilePickerActivity) is started, which creates the actual file picker activity using Intent.CreateChooser(). This activity then gets suspended when changing to another app, e.g. the "Downloads" app. From there the original app could be activated using IntentFilter, e.g. for a specific file type or custom URL schema. Then OnNewIntent is called, and due to the Activity's attribute "LaunchMode = LaunchMode.SingleTask" the initial activity is shown (e.g. MainActivity for Forms apps). In all this, the file picker plugin doesn't get notified that the file picker isn't shown anymore, and the next PickFile() call throws the exception mentioned.
Honestly, I don't know exactly how to fix that. The Interlocked.CompareExchange() call that throws the exception stores a TaskCompletionSource in a static variable that is needed to report back the task result (FileData). I guess this was done to prevent concurrent access to the TaskCompletionSource. Maybe it works to just cancel the old TaskCompletionSource and continue running. What are your thoughts?
Another ugly issue appears when permissions have to be requested. Then the temporary FilePickerActivity is shown, and it uses the default theme. The best would be if we could use the current activity (e.g. using the CurrentActivity plugin) and request the permission using the Permissions activity. Maybe it's time to port this plugin over to Xamarin.Essentials...
Well, I experimented a bit, and added the following code to PickFileAsync() in FilePickerImplementation.android.cs:
...
var id = this.GetRequestId(); // <-- existing code
var previousTcs = Interlocked.Exchange(ref this.completionSource, null);
if (previousTcs != null)
{
previousTcs.TrySetCanceled();
}
var ntcs = new TaskCompletionSource<FileData>(id); // <-- existing code
...
With this, the second call to PickFile() correctly starts file picking. Unfortunately the previous PickFile() call is still pending, and it gets a TaskCanceledException. So the user would have to catch and ignore that exception, which is not optimal.
@ChristianKleineidam Can you compile the FilePicker from source, add the lines and try out if that works with your app? Thanks!
Currently, the packages that I'm using in my App come directly from nuget. I'm not exactly sure how to go about using the self-compiled FilePicker instead.
When compiling the FilePicker I get:
1>------ Build started: Project: Plugin.FilePicker.Abstractions, Configuration: Release Any CPU ------
1>FileData.cs(94,28,94,45): warning CS0618: 'FileData.ReadFully(Stream)' is obsolete: 'ReadFully() is an implementation detail, please use DataArray or GetStream()'
1>Plugin.FilePicker.Abstractions -> C:\Users\Christian\Source\Repos\FilePicker-Plugin-for-Xamarin-and-Windows-1\src\Plugin.FilePicker\Plugin.FilePicker.Abstractions\bin\Release\netstandard1.0\Plugin.FilePicker.Abstractions.dll
1>Done building project "Plugin.FilePicker.Abstractions.csproj".
2>------ Build started: Project: Plugin.FilePicker, Configuration: Release Any CPU ------
2>Plugin.FilePicker -> C:\Users\Christian\Source\Repos\FilePicker-Plugin-for-Xamarin-and-Windows-1\src\Plugin.FilePicker\Plugin.FilePicker\bin\Release\netstandard1.0\Plugin.FilePicker.dll
========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
Where do I have to move the resulting files?
It's easy. Remove the NuGet package and add the ner standard Plugin.FilePicker.dll to your Forms project and the MonoAndroid90 version to your Android project. About the warning message... Are you still using the develop branch? Latest development is on matter.
@ChristianKleineidam Can you test your app with version 2.1.17-beta if it works for you now? I tested with my app and the exception is gone; instead the first PickFile() call is returning null, which I handle with showing nothing to the user. Thanks!
@vividos The core functionality is broken on Android. When I try to select a file I get the error:
[0:] Java.Lang.SecurityException: Permission Denial: opening provider com.estrongs.android.pop.app.FileContentProvider from ProcessRecord{9d18894 32502:com.ra_micro.dekrypter/u0a106} (pid=32502, uid=10106) that is not exported from uid 10159
at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00089] in <fdf05f528e174febb3e55b587dbab368>:0
at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0001f] in <fdf05f528e174febb3e55b587dbab368>:0
at Android.Content.ContentResolver.Query (Android.Net.Uri uri, System.String[] projection, System.String selection, System.String[] selectionArgs, System.String sortOrder) [0x000aa] in <2960acf2eeb24d88b5230e1e8afbdc2e>:0
at Plugin.FilePicker.IOUtil.GetDataColumn (Android.Content.Context context, Android.Net.Uri uri, System.String selection, System.String[] selectionArgs) [0x00015] in D:\a\1\s\src\Plugin.FilePicker\Android\IOUtil.android.cs:151
at Plugin.FilePicker.IOUtil.GetPath (Android.Content.Context context, Android.Net.Uri uri) [0x001f9] in D:\a\1\s\src\Plugin.FilePicker\Android\IOUtil.android.cs:115
at Plugin.FilePicker.FilePickerActivity.OnActivityResult (System.Int32 requestCode, Android.App.Result resultCode, Android.Content.Intent data) [0x0003c] in D:\a\1\s\src\Plugin.FilePicker\Android\FilePickerActivity.android.cs:147
--- End of managed Java.Lang.SecurityException stack trace ---
java.lang.SecurityException: Permission Denial: opening provider com.estrongs.android.pop.app.FileContentProvider from ProcessRecord{9d18894 32502:com.ra_micro.dekrypter/u0a106} (pid=32502, uid=10106) that is not exported from uid 10159
at android.os.Parcel.readException(Parcel.java:1684)
at android.os.Parcel.readException(Parcel.java:1637)
at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:4208)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:5509)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2314)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1520)
at android.content.ContentResolver.query(ContentResolver.java:518)
at android.content.ContentResolver.query(ContentResolver.java:475)
at md54fe4aef201482be7d95d72c0c9bf0b33.FilePickerActivity.n_onActivityResult(Native Method)
at md54fe4aef201482be7d95d72c0c9bf0b33.FilePickerActivity.onActivityResult(FilePickerActivity.java:47)
at android.app.Activity.dispatchActivityResult(Activity.java:6981)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4105)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4152)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1537)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:179)
at android.app.ActivityThread.main(ActivityThread.java:6152)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
com.estrongs.android.pop.app seems to be the app "ES File Explorer/Manager PRO", whose content provider throws an exception. My guess is that this is a different issue. Could you test your original use case with some other provider, e.g. Google Drive, OneDrive or just files from the Download folder? Thanks! You can of course open a new issue for the "ES File Explorer/Manager PRO" app.
Okay, I tested again and it works with Google Drive. I think the update is good to go.
I remembered that it worked in a previous version with ES File Explorer and another File Manager but it seems like both apps now stopped working even with the old version of FilePicker, so the update of the FilePicker doesn't seem to be an issue.
Thanks for confirming!
@vividos we want to go live with this one?
Expected Behavior
When the user clicks on the button that starts the file picker button, the file picker should always be started and the user should get the dialog.
Actual Behavior
The file picker throws
InvalidOperationException("Only one operation can be active at a time");
Steps to Reproduce the Problem
InvalidOperationException("Only one operation can be active at a time");
Instead of throwing
InvalidOperationException("Only one operation can be active at a time");
the file-picker plugin should end the existing operation and create a new active operation.Specifications