Forum Xamarin.Android
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

Video upload fails with access to storage denied error

Hi everyone,
I feel like I am missing something very simple you can help me with.

I have an app that allows users to upload a Video file from their gallery. Things work fine on other devices with API level 24 as well as API level 22.
When I run it on a cell phone running API level 29, it fails with this error:
System.UnauthorizedAccessException: 'Access to the path "/storage/emulated/0/DCIM/Camera/20201017_232507.mp4" is denied.'
From all my checks, runtime access is granted and the manifest includes:

I just can't figure out why it is failing on this device, but not the older ones!
I would really appreciate some advice here!

Here is what I am doing:

First, I check the API level. If it is more than 22, I request permission:

 //Get permissions at runtime
            if (Build.VERSION.SdkInt >= BuildVersionCodes.M)
            {
                RequestPermission();
            }

Here is my request permission:

        void RequestPermission()
        {
            string PermissionGranted;
            if (CheckSelfPermission(Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted)
            {
                PermissionGranted = Permission.Granted.ToString();
                Android.Widget.Toast.MakeText(this, "<app> does NOT have access to storage...", Android.Widget.ToastLength.Short).Show();
                ActivityCompat.RequestPermissions(this, new String[] { Manifest.Permission.ReadExternalStorage }, 5000);
            }
            else
            {
                PermissionGranted = Permission.Granted.ToString();
                Android.Widget.Toast.MakeText(this, "<app> HAS access to storage...", Android.Widget.ToastLength.Short).Show();               
            }
        }

Then, this is my permissions result:

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults)
        {
            if (requestCode == 5000)
            {
                // Received permission result for camera permission.
                Log.Info("VideoUpload", string.Format("Received response for Storage permission request. permissions: {0} - grant: {1}",permissions[0],grantResults[0]));

                // Check if the only required permission has been granted
                if (grantResults.Length == 1 && grantResults[0] == Permission.Granted)
                {
                    Android.Widget.Toast.MakeText(this, "Storage access granted...", Android.Widget.ToastLength.Short).Show();
                }
                else
                {
                    Android.Widget.Toast.MakeText(this, "Storage access NOT granted...", Android.Widget.ToastLength.Short).Show();
                }
            }
            else
            {
                base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            }
        }

Then, here is the actual code I run to do what i need to:

       private void BrowseVideoButton_Click(object sender, EventArgs e)
        {
            if (ContextCompat.CheckSelfPermission(this, Manifest.Permission.ReadExternalStorage) != (int)Permission.Granted)
            {
                CM.AlertDialog(this, "Acess Denied", "Sorry, it looks like this application does not have the necessary permissions to access storage.", "OK");
            }
            else
            {
                PhotoMode = "GALLERY";
                Intent = new Intent();
                Intent.SetType("video/*");
                Intent.SetAction(Intent.ActionGetContent);
                StartActivityForResult(Intent.CreateChooser(Intent, "Select Video"), PickImageId);
            }
        }

        async protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
        {
            if ((requestCode == PickImageId) && (resultCode == Result.Ok) && (data != null))
            {
                try
                {
                     if (PhotoMode == "GALLERY")
                    {

                        uri_G = data.Data;
                        Task mytask = new Task(delegate { SelectPhoto(requestCode, resultCode, data); });
                        mytask.Start();
                        await mytask;
                        Android.Widget.Toast.MakeText(this, "Video is ready for upload...", Android.Widget.ToastLength.Short).Show();
                    }
                }
                catch (Exception ex)
                {
                    CM.AlertDialog(this, "Error", "An error occurred while processing your request. Please contact IT support with this error: " + ex.Message, "OK");
                }
            }

        }



        private void SelectPhoto(int requestCode, Result resultCode, Intent data)
        {
            RunOnUiThread(Action);
        }
        private void Action()
        {
            try
            {
                Android.Net.Uri uri = uri_G;
                string filePath = null;

                if (DocumentsContract.IsDocumentUri(this, uri))
                {
                    string docId = DocumentsContract.GetDocumentId(uri);
                    if (uri.Authority.Equals("com.android.providers.media.documents"))
                    {
                        string id = docId.Split(":")[1];
                        string selection = MediaStore.Video.Media.InterfaceConsts.Id + " = " + id;
                        filePath = getVideoPath(MediaStore.Video.Media.ExternalContentUri, selection);
                    }
                    else if (uri.Authority.Equals("com.android.providers.downloads.documents"))
                    {
                        Android.Net.Uri contentUri = ContentUris.WithAppendedId(Android.Net.Uri.Parse("content://downloads/public_downloads"), long.Parse(docId));
                        filePath = getVideoPath(contentUri, null);
                    }

                }
                else if (uri.Scheme.Equals("content"))
                {
                    filePath = getVideoPath(uri, null);
                }
                else if (uri.Scheme.Equals("file"))
                {
                    filePath = uri.Path;
                }

                var bytes = System.IO.File.ReadAllBytes(filePath);
//code fails here. It says System.UnauthorizedAccessException: 'Access to the path "/storage/emulated/0/DCIM/Camera/20201017_232507.mp4" is denied.'


            }
            catch (Exception ex)
            {
                CM.AlertDialog(this, "Error", "An error occurred while processing the video file. Please contact IT with this error message: " + ex.Message, "OK");
            }
        }

        private string getVideoPath(Android.Net.Uri uri, string selection)
        {
            String path = null;

            var cursor = this.ContentResolver.Query(uri, null, selection, null, null);
            if (cursor != null)
            {
                if (cursor.MoveToFirst())
                {
                    path = cursor.GetString(cursor.GetColumnIndex(MediaStore.Video.Media.InterfaceConsts.Data));
                }
                cursor.Close();
            }
            return path;
        }

Best Answer

Answers

  • asusmaniasusmani Member ✭✭
    Actually, right after I posted this, that is exactly what I did and it worked!

    So added the requestlegacyexternalstorage To the manifest and lowered the target api.

    Thanks!
Sign In or Register to comment.