22

So I'm trying to create a file and I'm getting System.UnauthorizedAccessException: Access to the path "/DownloadJitters" is denied. I'm not sure if it's a permissions thing (I've tried a write to external storage in case but that didn't work) or something else. Also I'm trying to figure out a good place to write these files as I would like them not to be easily found. Any ideas? Here's the code as well :

public void favouriteList(MainActivity av, Ordering o, string favouriteName, string totalCost, JittersListView jlv)
    {
        //Checks Directory exists
        if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
        {
            Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
            File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
        }

        if (File.Exists(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt") == false)
        {
            var fav = File.Create(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt");
            fav.Close();
            string file = Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/" + favouriteName + ".txt";
            string added = null;
            int current = 0;
            while (true)
            {
                if (current < jlv.Count)
                {
                    JittersListItem jli = jlv[current];
                    added += jli.Top + "|" + jli.Bottom + "|" + jli.itemPic + "|" + jli.itemDes + System.Environment.NewLine;
                    current++;
                }
                else
                {
                    break;
                }
            }
            File.AppendAllText(file, favouriteName + "|" + totalCost + added);
        }
        else
        {
            new AlertDialog.Builder(av)
                    .SetMessage("Please use a different name, this one has been taken.")
                    .Show();
        }
    }
Euan Hollidge
  • 587
  • 2
  • 5
  • 21

5 Answers5

25

First of all add this permissions to you Manifest:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Since Android 6.0 (API 23) you need also to request the permissions manually, add this code on your MainActivity.cs on your Xamarin.Android project:

if ((ContextCompat.CheckSelfPermission(this, Manifest.Permission.WriteExternalStorage) != (int)Permission.Granted)
            || (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted))
        {
            ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, REQUEST);
        }

Since Android 10 you may also need to add android:requestLegacyExternalStorage attribute to your Manifest like this:

<application android:requestLegacyExternalStorage="true" />

UPDATE

After doing all this maybe you still get the exception on Android 11 or greather If you try save a file on any path of the SDCARD, you can only use the EXTERNAL STORAGE and INTERNAL STORAGE paths of your App by default.

PRIVATE EXTERNAL STORAGE PATH OF YOUR APP:

Android.App.Application.Context.GetExternalFilesDir(null).AbsolutePath

PRIVATE INTERNAL STORAGE PATH OF YOUR APP:

System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData)

If you need to access to any path of the SDCARD on Android 11 or greather, you should ask for manage all files access permission.

private void RequestStorageAccess()
{
    if (!Environment.IsExternalStorageManager) 
    {
        StartActivityForResult(new Intent(Android.Provider.Settings.ActionManageAllFilesAccessPermission), 3);
    }
}

This will pop up an Activity listing all the Apps that have access to all file access permission, the user should tap your App and enable the permission.

But in order to make your App appear there, you should add this permission to your manifest:

<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>

And you also need to make sure that you are compiling your App with Android 11 or greather.

enter image description here

Here you can see the permission Activity:

enter image description here

enter image description here

For some reason I was able to save files to documents folder without this permission, but after deleting the files I was not able to create again the same files with the same name, also I was not able to list all the files, this is why this pop-up is needed even to use the documents directory.

Use manage all files permission only in case that you really need

If you want to publish your App in Google Play with the "Manage All Files Permission", you will have to Fill a "Permissions Declaration Form" on Google Play, because it is considered a "high-risk or sensitive permissions" here is more information, and Google should approve it:

https://support.google.com/googleplay/android-developer/answer/9214102?hl=en#zippy=

When to ask for this permission:

https://support.google.com/googleplay/android-developer/answer/10467955?hl=en

Led Machine
  • 7,122
  • 3
  • 47
  • 49
  • This actually lead me to a unfortunate debug & fast deployment issue. This issue came when manifest file edited with & related lines. – Harish Patil Mar 27 '22 at 08:59
13

Ok I Fixed it by changing the saving location to System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal)

Don't ask me why that worked when they need the same permissions but it did.

Euan Hollidge
  • 587
  • 2
  • 5
  • 21
3

Xamarin.Forms (Android solution)

MainActivity.cs

  • For apps that target Android 5.1(API level 22) or lower, there is nothing more that needs to be done.
  • Apps that will run on Android 6.0(API 23 level 23) or higher should ask Run time permission checks.
protected override void OnCreate(Bundle bundle)
{
    if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
    {
        if (!(CheckPermissionGranted(Manifest.Permission.ReadExternalStorage) && !CheckPermissionGranted(Manifest.Permission.WriteExternalStorage)))
        {
            RequestPermission();
        }
    }
    LoadApplication(new App());
}

private void RequestPermission()
{
    ActivityCompat.RequestPermissions(this, new string[] { Manifest.Permission.ReadExternalStorage, Manifest.Permission.WriteExternalStorage }, 0);
}

public bool CheckPermissionGranted(string Permissions)
{
    // Check if the permission is already available.
    if (ActivityCompat.CheckSelfPermission(this, Permissions) != Permission.Granted)
    {
        return false;
    }
    else
    {
        return true;
    }
}
Divyesh
  • 2,085
  • 20
  • 35
  • This should be marked as perfect answer, as other answers lead me towards few more errors related to fast deploymnet. Thanks Divyesh_08 worked for me. – Harish Patil Mar 27 '22 at 08:58
0

This looks like a copy and paste error - you should learn to refactor common code and expressions into one value and reuse it.

//Checks Directory exists
if (File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt") == false)
{
    Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");
    File.Create(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt");
}

Let's assume Android.OS.Environment.DirectoryDownloads has the value /Downloads. Now go through the code line by line (you should really do this with a debugger):

File.Exists(Android.OS.Environment.DirectoryDownloads + "/Jitters/FavouritesListAdded.txt")

The parameter value here will be "/Downloads/Jitters/FavouritesListAdded.txt" - OK

Directory.CreateDirectory(Android.OS.Environment.DirectoryDownloads + "Jitters/FavouriteList/");

There's no leading slash on the literal string here, so the value will be: /DownloadsJitters/FavouriteList - I'm guessing you probably meant it to be /Downloads/Jitters/FavouriteList.

Rather than making sure slashes are added to all 6 path expressions in your code - just create one variable with the path value and reuse it.

adelphus
  • 10,116
  • 5
  • 36
  • 46
0

If you are still getting UnauthorizedAccessException for write or read file in Xamarin Android. I just written article to resolve it http://bsubramanyamraju.blogspot.com/2019/12/resolved-unauthorizedaccessexception.html