How to write (or overwrite) to file after using the OpenDocumentTree picker directory picker?

Axiixa 21 Reputation points
2022-03-27T02:32:07.543+00:00

My app creates a text document which I would like to write to external public shared storage ("Documents/myApp/Reports") with the intention that it remains accessible and write-able even after the user uninstalls/reinstalls the app. At runtime (after a reinstall) the user sees an OpenDocumentTree picker and chooses the destination folder. However, the file is still not accessible and throws an "Access denied..." error at the File.WriteAllText statement on my ActivityOnResult().

Min API = 6 and target API = 11.

I have no idea what I am doing incorrectly. Any help would be SO appreciated! Any code that you would be willing to post for a similar action would be very helpful!

This is the ActivityOnResult() code (following StartActivityForResult(myIntent, requestReportAccess)):

protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);

    if (requestCode == requestReportAccess)
    {
        if (resultCode == Result.Ok)
        {
            if (data != null)
            {
                // I can get flags here
                var takeFlags = data.Flags & ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission;

                // I can get the uri here
                Android.Net.Uri myUri = data.Data;

                // This works until user uninstalls/reinstalls app
                try 
                {
                    var externalCopyOfReport = System.IO.Path.Combine(externalReportFolder, Resources.GetString(Resource.String.MileageReportFile));

                    //Write the text to a file. Creates a new file, write the contents to the file, and then closes the file. If the target file already exists, it is overwritten.
                    File.WriteAllText(externalCopyOfReport, mileageReportAsString.ToString());
                }
                catch (Exception ex)
                {
                    // Do something here
                }

            }
        }
    }
}
Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,206 questions
{count} votes

1 answer

Sort by: Most helpful
  1. Axiixa 21 Reputation points
    2022-04-03T03:33:35.147+00:00

    Sir, I have been working on this for several hours and I am very confused. I hope you can help!

    My OnActivityResult method fails with this error:

    Java.Lang.IllegalArgumentException: Invalid URI: content://com.android.externalstorage.documents/tree/primary%3ADocuments%2FVehicleLog

    In the debugger MyURI shows as:

    {content://com.android.externalstorage.documents/tree/primary%3ADocuments%2FVehicleLog}

    This the provider in my Manifest:

            <provider 
                android:name="android.support.v4.content.FileProvider" 
                android:authorities="${applicationId}.provider" 
                android:exported="false" 
                android:grantUriPermissions="true">
                <meta-data android:name="android.support.FILE_PROVIDER_PATHS" 
                    android:resource="@xml/provider_paths" />
            </provider>
    

    This is my provider_paths xml file:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
    
      <external-path name="VL_External" path="Documents/VehicleLog/" />
    
      <cache-path name="myCache" path="Reports/" />
    </paths>
    

    My Intent (I would like to gain read/write access to all the files and folders in VehicleLog and its subdirectories):

                            Intent myIntent = new Intent(Android.Content.Intent.ActionOpenDocumentTree);    
                            StartActivityForResult(myIntent, requestReportAccess);
    

    After selecting the /Storage/Emulated/0/Documernts/VehicleLog folder in the folder picker my OnActivityResult fails on line 26 (see the URI above):

    protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
            {
                base.OnActivityResult(requestCode, resultCode, data);
    
                if (requestCode == requestReportAccess)
                {
                    if (resultCode == Result.Ok)
                    {
                        if (data != null)
                        {
                            var takeFlags = data.Flags & ActivityFlags.GrantWriteUriPermission | ActivityFlags.GrantReadUriPermission;
    
                            // URI to the Reports directory
                            Android.Net.Uri myUri = data.Data;
    
                            ContentResolver.TakePersistableUriPermission(myUri, takeFlags);
    
                            // Retry the write
                            try
                            {
                                var targetPath = publicReportFolder;  // This is: /storage/emulated/0/Documents/VehicleLog/Reports
    
                                //Write the text to a file. Creates a new file, write the contents to the file, and then closes the file.
                                //If the target file already exists, it is overwritten.
                                const int bufferSize = 1024;
                                using (var inputStream = ContentResolver.OpenInputStream(myUri))
                                {
                                    using (var outputStream = File.Create(targetPath))
                                    {
                                        var buffer = new byte[bufferSize];
                                        while (true)
                                        {
                                            var count = inputStream.Read(buffer, 0, bufferSize);
                                            if (count > 0)
                                            {
                                                outputStream.Write(buffer, 0, count);
                                            }
                                            if (count < bufferSize) break;
                                        }
                                    }
                                }
                            }
                            catch (Exception ex)
                            {
                                Toast.MakeText(this, "inform the user...", ToastLength.Short).Show();
                            };
                        }
                    }
                }
            }
    

    Sir, any help you can provide is greatly appreciated. Thank you for all your time!