Search code examples
androidxamarin.formsxamarin.androidandroid-10.0filepicker

Xamarin.Plugin.FilePicker - content:/com.android.providers.downloads.documents Problem


I am using Xamarin.Plugin.FilePicker (version is: 2.1.41). It gives me like this path in android 10 devices: content://com.android.providers.downloads.documents/document/3025 or content://com.android.providers.downloads.documents/document/msf:14

I am trying to get real file path with this way:

public class StorageFolderPath : IStorageFolderPath
{
    public string SDCardPath(string path)
    {
        Android.Net.Uri uri = Android.Net.Uri.Parse(path);
            bool isKitKat = Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Kitkat;

        if (isKitKat && DocumentsContract.IsDocumentUri(MainActivity.MainContext, uri))
        {
            if (isExternalStorageDocument(uri))
            {
                //...
            }
            else if (isDownloadsDocument(uri))
            {
                string id = DocumentsContract.GetDocumentId(uri);
                Android.Net.Uri contentUri = ContentUris.WithAppendedId(
                                        Android.Net.Uri.Parse("content://downloads/public_downloads"), ContentUris.ParseId(uri));
                return getDataColumn(MainActivity.MainContext, contentUri, null, null);
            }
            //...
        }
    }

    public static String getDataColumn(Context context, Android.Net.Uri uri, String selection, String[] selectionArgs)
        {
            ICursor cursor = null;
            String column = "_data";
            String[] projection =
            {
                column
            };

            try
            {
                cursor = context.ContentResolver.Query(uri, projection, selection, selectionArgs, null);
                if (cursor != null && cursor.MoveToFirst())
                {
                    int index = cursor.GetColumnIndexOrThrow(column);
                    return cursor.GetString(index);
                }
            }
            finally
            {
                if (cursor != null)
                    cursor.Close();
            }
            return null;
        }

    public static bool isDownloadsDocument(Android.Net.Uri uri)
        {
            return "com.android.providers.downloads.documents".Equals(uri.Authority);
        }

    public bool DownloadsDocumentControl(string path)
        {
            Android.Net.Uri uri = Android.Net.Uri.Parse(path);
            return isDownloadsDocument(uri);
        }
}

Shared Codes:

public async Task UploadFileAsync(UploadRequest upload_request)
{
    string newPath = "";
    if (Device.RuntimePlatform == Device.Android && 
       (DependencyService.Get<IStorageFolderPath>().ExternalStorageDocumentControl(upload_request.Properties.FilePath) ||
       DependencyService.Get<IStorageFolderPath>().DownloadsDocumentControl(upload_request.Properties.FilePath)))
        newPath = DependencyService.Get<IStorageFolderPath>().SDCardPath(upload_request.Properties.FilePath);
    else
        newPath = upload_request.Properties.FilePath;

    Stream stream = File.OpenRead(newPath);
    //...
}

When I do this: If path is content://com.android.providers.downloads.documents/document/msf:14, app throws this error:

{Java.Lang.NumberFormatException: For input string: "msf:14"
  at Java.Interop.JniEnvironment+StaticMethods.CallStaticLongMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0006e] in <42748fcc36b74733af2d9940a8f3cc8e>:0 
  at Java.Interop.JniPeerMembers+JniStaticMethods.InvokeInt64Method (System.String encodedMember, Java.Interop.JniArgumentValue* parameters) [0x00018] in <42748fcc36b74733af2d9940a8f3cc8e>:0 
  at Android.Content.ContentUris.ParseId (Android.Net.Uri contentUri) [0x00031] in <7d2292394f8c488b97f5bc2a0ac0240d>:0 
  at FileOrbis.Droid.Services.StorageFolderPath.SDCardPath (System.String path) [0x0017c] in C:\Users\Deytek\Desktop\GitWorkspace\FileorbisMobil\FileOrbis\FileOrbis.Android\Services\StorageFolderPath.cs:91 
  at FileOrbis.Services.AllFileUploadService.UploadFileAsync (FileOrbis.Models.RequestModels.UploadRequest upload_request) [0x000ae] in C:\Users\Deytek\Desktop\GitWorkspace\FileorbisMobil\FileOrbis\FileOrbis\Services\AllFileUploadService.cs:69 
  --- End of managed Java.Lang.NumberFormatException stack trace ---
java.lang.NumberFormatException: For input string: "msf:14"
    at java.lang.Long.parseLong(Long.java:594)
    at java.lang.Long.parseLong(Long.java:636)
    at android.content.ContentUris.parseId(ContentUris.java:89)
}

(App crashes in this line: (on ContentUris.ParseId(uri))

Android.Net.Uri contentUri = ContentUris.WithAppendedId(
                                        Android.Net.Uri.Parse("content://downloads/public_downloads"), ContentUris.ParseId(uri));)

If path is content://com.android.providers.downloads.documents/document/3025 , app throws this error:

{System.IO.DirectoryNotFoundException: Could not find a part of the path "/content:/com.android.providers.downloads.documents/document/3025".
  at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x00177] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.IO/FileStream.cs:223 
  at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/corlib/System.IO/FileStream.cs:91 
  at (wrapper remoting-invoke-with-check) System.IO.FileStream..ctor(string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare)
  at System.IO.File.OpenRead (System.String path) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.IO.FileSystem/src/System/IO/File.cs:266 
  at FileOrbis.Services.AllFileUploadService.UploadFileAsync (FileOrbis.Models.RequestModels.UploadRequest upload_request) [0x000de] in C:\Users\Deytek\Desktop\GitWorkspace\FileorbisMobil\FileOrbis\FileOrbis\Services\AllFileUploadService.cs:73 }

NOTE:

I added to manifest file:

<application android:requestLegacyExternalStorage="true">
</application>

What must I do on Android 10 devices for this problem? (I don't encounter with this problem on all android 10 devices. I encounter on Samsung Galaxy S10 - emulator, Huawei P20 Lite - real phone or Galaxy S20 - real phone)


Solution

  • What the blackapps said is true. Already FilePicker plugin has notified that is possible. But I see it now.

    Getting actual path from content scheme is not true way and it is not possible. That's why I have to write if condition on my code like that:

    Stream stream = null;
    if (Device.RuntimePlatform == Device.Android && DependencyService.Get<IStorageFolderPath>().DownloadsDocumentControl(newPath))
         stream = DependencyService.Get<IStorageFolderPath>().GetFileFromUri(newPath);
    else
         stream = File.OpenRead(newPath);
    
    public Stream GetFileFromUri(string path)
    {
         Android.Net.Uri uri = Android.Net.Uri.Parse(path);
         var stream = MainActivity.MainContext.ContentResolver.OpenInputStream(uri);
         return stream;
    }