xamarin / Essentials

Xamarin.Essentials is no longer supported. Migrate your apps to .NET MAUI, which includes Maui.Essentials.
https://aka.ms/xamarin-upgrade
Other
1.52k stars 506 forks source link

[Bug] Storage permissions Android 13 api 33 #2041

Open ChrisDox opened 2 years ago

ChrisDox commented 2 years ago

Description

I have a strange thing when My TARGET ANDROID VERSION is Android 13.0 (API Level 33)

When i use CrossMedia plugin, i want to ask manual permissions to external storage When i do

await Permissions.RequestAsync<Permissions.StorageRead>(); OR
await Permissions.RequestAsync<Permissions.StorageWrite>();

--> NO displayAlert comes to ask permission and the result is always "Denied"

for other asking like Camera, the displayAlert comes normally and it's ok.

To test i use xamarin essential mediaPicker when i do var photo = await MediaPicker.CapturePhotoAsync();

First i have normal alert with "Allow ... to take pictures an record video" After that i have a PermissionException

StorageWrite permission was not granted: Denied
at Xamarin.Essentials.Permissions.EnsureGrantedAsync[TPermission] () [0x00066] in D:\a\_work\1\s\Xamarin.Essentials\Permissions\Permissions.shared.cs:29 
  at Xamarin.Essentials.MediaPicker.PlatformCaptureAsync (Xamarin.Essentials.MediaPickerOptions options, System.Boolean photo) [0x0007e] in D:\a\_work\1\s\Xamarin.Essentials\MediaPicker\MediaPicker.android.cs:59 

.... And NOT storage permission asking alert

WHEN My TARGET ANDROID VERSION is Android 12.1 (API Level 32) ALL WORKS FINE

In my manifest i have

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA" />
        .....
        <queries>
            <intent>
              <action android:name="android.media.action.IMAGE_CAPTURE" />
            </intent>
         </queries>

Expected Behavior

Alert with Storage authorization asking

Actual Behavior

PermissionException StorageWrite permission was not granted: Denied

Basic Information

giuseppenovielli commented 1 year ago

Hi guys, i have analyzed upgrade Android app's to API 33. So i want to clarify some aspect that i have met and hope to be of help to all of you.

Topic -> New Granular media permissions

ANDROID If your app targets Android 13 or higher and needs to access media files that other apps have created, you must request one or more of the following granular media permissions instead of the READ_EXTERNAL_STORAGE permission: READ_MEDIA_IMAGES READ_MEDIA_VIDEO READ_MEDIA_AUDIO

XAMARIN So if you need READ_EXTERNAL_STORAGE in your app you must replace it with new granular permissions.

Warning! if you need a simple Photo, Video or File picker, check if you really need permission -> READ_EXTERNAL_STORAGE https://stackoverflow.com/questions/72586826/is-permission-needed-when-choosing-image-from-gallery-on-android

So if you must use READ_EXTERNAL_STORAGE: 1)Xamarin.Essentials will never supported new Granular media permissions as says this topic -> https://github.com/xamarin/Essentials/pull/2065

So you must implement yourself this new three permissions, following this guidelines: https://learn.microsoft.com/en-us/xamarin/essentials/permissions?tabs=android#extending-permissions

SharedProject 1)Write general permission interface

public interface ICustomPermission
    {
        (string androidPermission, bool isRuntime)[] RequiredPermissions { get; }
        Task<PermissionStatus> CheckStatusAsync();
        Task<PermissionStatus> RequestAsync();
    }

2) inheritance from ICustomPermission for every new permission

public interface IReadMediaImagesPermission : ICustomPermission
    {
    }

AndroidProject 3) Define the follow class for every permission, and implement interface

[assembly: Xamarin.Forms.Dependency(typeof(<YourAndroidNameSpace>.Droid.ReadMediaImagesPermission))]    
namespace <YourAndroidNameSpace>.Droid    
{    
   public class ReadMediaImagesPermission : BasePlatformPermission, IReadMediaImagesPermission
    {
        public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
            new (string, bool)[] { (Manifest.Permission.ReadMediaImages, true) };
    }  
   }    
}

SharedProject 4) Use DependencyService for use new permissions as follow

var readMediaImagePermission = DependencyService.Get<IReadMediaImagesPermission>();
var status = await readMediaImagePermission.CheckStatusAsync();
if (status != PermissionStatus.Granted)
{
    status = await readMediaImagePermission.RequestAsync();
}

Topic -> I use Xamarin.Essentials Media Picker

In this case you only update Xamarin.Essentials v1.7.5. -> https://github.com/xamarin/Essentials/pull/2073

You don't need to add new granular permissions to Android manifests. Xamarin.Essentials Media Picker only need CAMERA permissions.

Topic -> WRITE_EXTERNAL_STORAGE

As Android developer docs says: https://developer.android.com/reference/android/Manifest.permission#WRITE_EXTERNAL_STORAGE _Note: If your app targets Build.VERSION_CODES.R or higher, this permission has no effect._

So from API 33 you MUST stop to ask WRITE_EXTERNAL_STORAGE permission. if you can review your code, ok or you can use the @Pastajello hack.

dartur123 commented 1 year ago

Thanks for the explanation @giuseppenovielli.

I can now call newly introduced permissions that are not being handled automatically.

Extending permissions help me achieve requesting the permissions.

runtimesoftware commented 1 year ago

@gjhdigital @alexkivikoski

Ok so listen here, how about a little trick here: Since on API 33 the WRITE_MEDIA_STORAGE is useless the OS is ignoring it-> setting the permission status as denied. If we only have to internally trick the Essentials to think it is granted it is not THAT hard, is it? There is a place to tell Essentials what is the status of requested permissions. We just have to tell some lies.

Of course you need to remember that if there are multiple permissions in the array you have to replace only the value for the WRITE_MEDIA_STORAGE. AND check if we are on API 33+, before I think the permission is doing something, things will get messy otherwise I guess. But otherwise its working. ;) Just tested. (really this hack took me like 5minutes to think of)

      `public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {

        if (permissions.ToList().Where(x => x == "WRITE_MEDIA_STORAGE") != null)
        {
            var grants= new Permission[] { Permission.Granted };
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grants);
        }
        else
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }`

I noticed a couple of corrections here, which may be useful:

  1. It should be "WRITE_EXTERNAL_STORAGE" instead of "WRITE_MEDIA_STORAGE"
  2. The LINQ should be permissions.ToList().Where(a => x => x.Contains("WRITE_EXTENRAL_STORAGE").Any() Since the actual permission text is android.permissions.WRITE_EXTERNAL_STORAGE and not just "WRITE_EXTERNAL_STORAGE" when matching in LINQ expression.
DS2107 commented 1 year ago

@runtimesoftware , can you give a working example?

christian-strydom commented 1 year ago

Hi guys, I landed on this thread because I kept getting an unauthorized access exception when trying to write even to internal app data on Android 13. So, I'm just posting my answer here in case anyone else has a similar issue. Hope it helps!

Ydvisual commented 1 year ago

For API33:

READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE are not used anymore

AND(more importantly)-

For example, when you call requestPermissions() for WRITE_EXTERNAL_STORAGE onRequestPermissionResult() will always return PERMISSION_DENIED Long story short, this is the correct and expected behavior for API33.

To prevent this on API33:

Change AndroidManifest.xml

From:

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

To:

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

This means when this app is installed on an Android device with an API higher than 18 (ie API33) the READ/WRITE_EXTERNAL_STORAGE permissions will be ignored as if it they were not even in the AndroidManifest.xml file.

Side note: Instead of maxSdkVersion="18", you could also choose "23" or even "32" I don't know what your xamarin/essentials software is but: This number depends on how your team is using READ/WRITE_EXTERNAL_STORAGE for previous versions of Android.

thisisthekap commented 1 year ago

As Xamarin.Forms is going to support Android 13 (a prerelease doing so is available at https://github.com/xamarin/Xamarin.Forms/releases/tag/beta-5.0.0-sr15-pre1), I think this issue should be reevaluated.

toumir commented 1 year ago

The bug remains and the pre release not fixing the issue

thisisthekap commented 1 year ago

@toumir Totally agree. My point was, that as Xamarin.Forms not starts to support Android 13, we should get the chance to include the Android 13 permission stuff into Xamarin.Essentials.

gjhdigital commented 1 year ago

dont you just have to do this inthe Android. AssemblyInfo.cs file?

[assembly: UsesPermission(Android.Manifest.Permission.ReadExternalStorage)] [assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)]

I dont see that being a Xamarin.Essentials thing. That would be like having Essentials add the ios infp.plist stuff.

MitchBomcanhao commented 1 year ago

Have you actually tried the essentials 1.8 preview build with android 13 support changes? It was released at the same time as the xamarin forms preview...

rmpQue commented 1 year ago

Still getting the same issue, xamarin.essentials.permissionexception: storageread permission was not granted: denied android api level 33 filepicker Please, let me know if any other solution for same.

chinochuy commented 1 year ago

Same issue here with camera, 1 - if I select (Don't allow permission) with Xamarin Essentials RequestPermission I get a Granted (very strange)

2 - If I use MediaPicker.CapturePhotoAsync(), app asking permissions and I select Don't allow option, app crash and I get this message (xamarin.essentials.permissionexception: storageread permission was not granted: denied android).

This happend with Android 13 only.

Saccomani commented 1 year ago

Same issue here, we request a permission from essentials and show granted, but when the app save on the disk we receive permission denied for storage write.

have any workaround to fix this?

We are still using Xamarin Forms.

I already try to put the granular permissions on manifest but still not working.

KamalKant-Tech commented 1 year ago

I have had this same problem and I fixed it by adding the below code in the manifest file for Android.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>
leffemedkniven commented 1 year ago

I have had this same problem and I fixed it by adding the below code in the manifest file for Android.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

You didn't need to change anything else?

KamalKant-Tech commented 1 year ago

I have had this same problem and I fixed it by adding the below code in the manifest file for Android.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

You didn't need to change anything else?

I have added another small check while checking the permission.

var permissionsNeeded = ['android.permission.READ_MEDIA_IMAGES', 'android.permission.READ_MEDIA_VIDEO'];

  if (Ti.Platform.version < 13) {
    permissionsNeeded = ['android.permission.READ_EXTERNAL_STORAGE', 'android.permission.WRITE_EXTERNAL_STORAGE'];
  }

  if (Ti.Android.hasPermission(permissionsNeeded)) {
        //somelogic
  } else {
    Ti.Android.requestPermissions(permissionsNeeded, function (e) {
     // Some custom logic of our app
     });
  }
zerokewl88 commented 1 year ago

@KamalKant-Tech - when adding your manifest to a API33, and then trying to access the permissions, i'm getting a exception - do you also get this ?

Manifest

  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
  <uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
  <uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

Calling Code

if (Double.Parse(Xamarin.Essentials.DeviceInfo.VersionString) >= 13)
                        {
                            var nStatus = await Permissions.CheckStatusAsync<Permissions.StorageWrite>();
                            if (nStatus != PermissionStatus.Granted)
                            {
                                nStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();
                            }

                            nStatus = await Permissions.CheckStatusAsync<Permissions.StorageRead>();
                            if (nStatus != PermissionStatus.Granted)
                            {
                                nStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                            }
                        }

Exception thrown

{Xamarin.Essentials.PermissionException: You need to declare using the permission: `android.permission.WRITE_EXTERNAL_STORAGE` in your AndroidManifest.xml
  at Xamarin.Essentials.Permissions+BasePlatformPermission.CheckStatusAsync () [0x0003c] in D:\a\_work\1\s\Xamarin.Essentials\Permissions\Permissions.android.cs:64 
  at Xamarin.Essentials.Permissions.CheckStatusAsync[TPermission] () [0x00000] in D:\a\_work\1\s\Xamarin.Essentials\Permissions\Permissions.shared.cs:9 
  at ....InitPermissionsViewModel.<CheckPermissionTimer_Elapsed>b__22_0 () [0x000a5] in .... }
KamalKant-Tech commented 1 year ago

I did not get this error in my case.

This permission android.permission.WRITE_EXTERNAL_STORAGE is not applicable for Android API 33 and above. Are you running this below 33 API levels?

zerokewl88 commented 1 year ago

@KamalKant-Tech thanks for the quick reply - i'm testing this again now, and ensure that it's API 33, and i'm still getting this exception.

image

{Plugin.Media.Abstractions.MediaPermissionException: Camera permission(s) are required.
  at Plugin.Media.MediaImplementation.TakePhotoAsync (Plugin.Media.Abstractions.StoreCameraMediaOptions options, System.Threading.CancellationToken token) [0x0008f] in d:\a\1\s\src\Media.Plugin\Android\MediaImplementation.cs:189 
  at .....TakePhoto () [0x0016e] in ....}
KamalKant-Tech commented 1 year ago

@KamalKant-Tech thanks for the quick reply - i'm testing this again now, and ensure that it's API 33, and i'm still getting this exception.

image

{Plugin.Media.Abstractions.MediaPermissionException: Camera permission(s) are required.
  at Plugin.Media.MediaImplementation.TakePhotoAsync (Plugin.Media.Abstractions.StoreCameraMediaOptions options, System.Threading.CancellationToken token) [0x0008f] in d:\a\1\s\src\Media.Plugin\Android\MediaImplementation.cs:189 
  at .....TakePhoto () [0x0016e] in ....}

Add this permission to your manifest file.

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

zerokewl88 commented 1 year ago

@KamalKant-Tech Sorry, i should've given my complete manifest - i already have this included as you have suggested, and that error was still being thrown.

My complete manifest looks like this now...

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="227" package="com.acme.org" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
    <application android:label="Induction" android:icon="@mipmap/icon" android:allowBackup="false">
        <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:usesCleartextTraffic="true" android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
        </provider>
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
    <uses-feature android:name="android.hardware.location" android:required="false" />
    <uses-feature android:name="android.hardware.location.gps" android:required="false" />
    <uses-feature android:name="android.hardware.location.network" android:required="false" />

  <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
  <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
  <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>

</manifest>
zerokewl88 commented 1 year ago

@KamalKant-Tech By any chance are you using ? image

zerokewl88 commented 1 year ago

Well, there seems to be a answer here at the bottom of this thread, and also some feedback from a member of Xamarin team, and their 'response' which was "As mentioned in https://github.com/xamarin/Essentials/pull/2063#issuecomment-1338106117 also about permissions, Xamarin.Essentials is in maintanance only and we're not looking to add new functionality unless we absolutely have to. New development will happen in .NET MAUI."

REF: https://github.com/xamarin/Essentials/pull/2065

zerokewl88 commented 1 year ago

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

  1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");
    
                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }
    
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
  2. Manifest File

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
    <uses-feature android:name="android.hardware.location" android:required="false" />
    <uses-feature android:name="android.hardware.location.gps" android:required="false" />
    <uses-feature android:name="android.hardware.location.network" android:required="false" />
    
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>

Credit to @WanftMoon on this post (https://github.com/xamarin/Essentials/pull/2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

leffemedkniven commented 1 year ago

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
2. Manifest File
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.CAMERA" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
  <uses-feature android:name="android.hardware.location" android:required="false" />
  <uses-feature android:name="android.hardware.location.gps" android:required="false" />
  <uses-feature android:name="android.hardware.location.network" android:required="false" />

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

Credit to @WanftMoon on this post (#2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

Did you get this working in a release-version? It works for me, but only in debug. As if somethings wrong with the manifest-file.

zerokewl88 commented 1 year ago

I published it to a release version, assuming it would work.. after testing with a debug version. I think I'll report back here and double check it's working post release.. thanks.

Kind Regards,

Jeffrey Holmes +66806075331


From: Daniel Halme Ståhlberg @.> Sent: Friday, September 15, 2023 6:20:02 PM To: xamarin/Essentials @.> Cc: Jeffrey Holmes @.>; Comment @.> Subject: Re: [xamarin/Essentials] [Bug] Storage permissions Android 13 api 33 (Issue #2041)

OK - it's a simple fix to get around this, and it's working ok for me on API33.

You only have to update the following method, and then add the manifest entries - and your camera operations are working again on API 33.

  1. Android Main Activity updat the following Method to this.. (This tricks Android to accept a permission on a platform where it's no longer used - lol )

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any())) { var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE"); var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

            if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
            if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
        }

        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
  1. Manifest File

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!--<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
    <uses-feature android:name="android.hardware.location" android:required="false" />
    <uses-feature android:name="android.hardware.location.gps" android:required="false" />
    <uses-feature android:name="android.hardware.location.network" android:required="false" />

Credit to @WanftMoonhttps://github.com/WanftMoon on this post (#2065https://github.com/xamarin/Essentials/pull/2065) for pointing me in the right direction. Camera operations restored in API 33 - thank you.

Did you get this working in a release-version? It works for me, but only in debug. As if somethings wrong with the manifest-file.

— Reply to this email directly, view it on GitHubhttps://github.com/xamarin/Essentials/issues/2041#issuecomment-1721107768, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ABJ4KG2FQD4UKZR2MGPHEFTX2Q2WFANCNFSM6AAAAAAQG6GEL4. You are receiving this because you commented.Message ID: @.***>

zerokewl88 commented 1 year ago

@leffemedkniven Hi there - i've just reinstalled a release version, and confirmed this method above works ok. Did you accept / create the permissions on startup ?

With what i have posted above, on startup, i have a check for permissions that requests the following.

try
                        {
                            cameraPermission = await CrossPermissions.Current.CheckPermissionStatusAsync<CameraPermission>();
                            if (cameraPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
                            {
                                var status = await CrossPermissions.Current.RequestPermissionAsync<CameraPermission>();
                            }
                        }
                        catch (Exception ex)
                        {
                            Crashes.TrackError(ex);
                        }

                        try
                        {
                            photosPermission = await CrossPermissions.Current.CheckPermissionStatusAsync<PhotosPermission>();
                            if (photosPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted)
                            {
                                var status = await CrossPermissions.Current.RequestPermissionAsync<PhotosPermission>();
                            }
                        }
                        catch (Exception ex)
                        {
                            Crashes.TrackError(ex);
                        }
leffemedkniven commented 1 year ago

cameraPermission = await CrossPermissions.Current.CheckPermissionStatusAsync(); if (cameraPermission != Plugin.Permissions.Abstractions.PermissionStatus.Granted) { var status = await CrossPermissions.Current.RequestPermissionAsync(); }

When adding this to the PageModel it doesn't pop-up at all, only in debug. Trying to add it to MainActivity.OnCreate doesn't work because it's not async.

Tried this in MainActivity aswell:

if (ActivityCompat.CheckSelfPermission(Xamarin.Essentials.Platform.CurrentActivity, Android.Manifest.Permission.ReadMediaImages) != Permission.Granted) { ActivityCompat.RequestPermissions(Xamarin.Essentials.Platform.CurrentActivity, new string[] { Android.Manifest.Permission.ReadMediaImages }, 100); }

It pops up in release, I allow it, but it doesn't work when trying to add pictures to external storage.

zerokewl88 commented 1 year ago

I actually don't request permission for ReadMediaImages explicity during runtime, this is handled already in my manifest file without having to request it.

The permissions above that i'm requesting i've already included in the above post.

HardikMadhwani commented 1 year ago

I have had this same problem and I fixed it by adding the below code in the manifest file for Android.

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>

does this mean that we don't need permission to write in android 13 onwards? we can directly write if required? if yes than how is this increasing security?

willisra83 commented 1 year ago

I actually don't request permission for ReadMediaImages explicity during runtime, this is handled already in my manifest file without having to request it.

The permissions above that i'm requesting i've already included in the above post.

So in the Android manifest, did you end up commenting out the old permissions (READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE)? And so this means you no longer need to check CrossPermissions.Current.CheckPermissionStatusAsync<StoragePermission>() on Android?

And you still need the code below also?

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

Do you need any other changes to fix it? And know if this would still work with a min SDK of <33 specified? Or did you just specify both min SDK and target SDK as 33?

luisrr1590 commented 1 year ago

I can confirm that this works for Android 13:

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults) { if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any())) { var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE"); var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

            if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
            if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
        }

        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

WanftMoon commented 1 year ago

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

Did you extend the permissions?

public interface IReadImagesVideoPermission
{
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}
public class ReadImagesVideoPermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IReadImagesVideoPermission   
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
        ("android.permission.READ_MEDIA_IMAGES", true),
        ("android.permission.READ_MEDIA_VIDEO", true)
    }.ToArray();
}
//this is a check for android 13+
if (DeviceInfo.Platform.Equals(DevicePlatform.Android) && DeviceInfo.Version.Major >= 13)
{
    var readImagesVideoPermission = DependencyService.Get<IReadImagesVideoPermission>();

    var permission = await readImagesVideoPermission.CheckStatusAsync();

    if (permission != PermissionStatus.Granted)
    {
        // Prompt the user with additional information as to why the permission is needed
        if (rationale != null) await rationale();

        permission = await readImagesVideoPermission.RequestAsync();
    }

    return permission;
}
craigwi commented 1 year ago

In addition to the manifest changes, I found it simpler to create a subclass of StorageRead and put the sdk check there:

        public class ReadMedia : Permissions.StorageRead
        {
#if __ANDROID__
            public override (string androidPermission, bool isRuntime)[] RequiredPermissions
            {
                get
                {
                    // after sdk 33 (Android 13), this requires read two media values
                    if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Tiramisu)
                    {
                        return new (string, bool)[] { ( Android.Manifest.Permission.ReadMediaImages, true), ( Android.Manifest.Permission.ReadMediaVideo, true) };
                    }

                    // otherwise, just use the original single permission read external storage
                    return base.RequiredPermissions;
                }
            }
#endif
        }

Then use ReadMedia permission class anywhere I would use StorageRead.

willisra83 commented 1 year ago

but, I cannot remove the old permissions from the Manifest nor set android:maxSdkVersion= because the plugin will fail when calling CheckStatusAsync.

Did you extend the permissions?

public interface IReadImagesVideoPermission
{
    Task<PermissionStatus> CheckStatusAsync();
    Task<PermissionStatus> RequestAsync();
}
public class ReadImagesVideoPermission : Xamarin.Essentials.Permissions.BasePlatformPermission, IReadImagesVideoPermission   
{
    public override (string androidPermission, bool isRuntime)[] RequiredPermissions => new List<(string androidPermission, bool isRuntime)> {
        ("android.permission.READ_MEDIA_IMAGES", true),
        ("android.permission.READ_MEDIA_VIDEO", true)
    }.ToArray();
}
//this is a check for android 13+
if (DeviceInfo.Platform.Equals(DevicePlatform.Android) && DeviceInfo.Version.Major >= 13)
{
    var readImagesVideoPermission = DependencyService.Get<IReadImagesVideoPermission>();

    var permission = await readImagesVideoPermission.CheckStatusAsync();

    if (permission != PermissionStatus.Granted)
    {
        // Prompt the user with additional information as to why the permission is needed
        if (rationale != null) await rationale();

        permission = await readImagesVideoPermission.RequestAsync();
    }

    return permission;
}

Know when this base permission class functionality would/should be needed? Because with just simply using the code below, I'm able to upload photos on Android 13, 12, and 10 just fine. So it seems the above code isn't needed (unless I'm missing a scenario)...

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");

                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }

            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }

My manifest

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"  />
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>
    <uses-permission android:name="android.permission.CAMERA" />
maxib0n commented 11 months ago

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }

    }
MrRob02 commented 11 months ago

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }

    }

Which is the Platform library?

maxib0n commented 11 months ago

This worked for me

public class AllFileSystemPermission : Permissions.BasePermission
    {
        public override Task<PermissionStatus> CheckStatusAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = Permissions.CheckStatusAsync<Permissions.StorageRead>().Result;
                var writeStatus = Permissions.CheckStatusAsync<Permissions.StorageWrite>().Result;

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return Task.FromResult(PermissionStatus.Granted);

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return Task.FromResult(PermissionStatus.Denied);

                return Task.FromResult(PermissionStatus.Unknown);
            }
            else
            {
                var manageStatus = Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage);
                return Task.FromResult(manageStatus == Permission.Granted ? PermissionStatus.Granted : PermissionStatus.Denied);
            }
        }

        public override async Task<PermissionStatus> RequestAsync()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                var readStatus = await Permissions.RequestAsync<Permissions.StorageRead>();
                var writeStatus = await Permissions.RequestAsync<Permissions.StorageWrite>();

                if (readStatus == PermissionStatus.Granted && writeStatus == PermissionStatus.Granted)
                    return PermissionStatus.Granted;

                if (readStatus == PermissionStatus.Denied || writeStatus == PermissionStatus.Denied)
                    return PermissionStatus.Denied;

                return PermissionStatus.Unknown;
            }
            else
            {
                if (Platform.AppContext.CheckSelfPermission(Android.Manifest.Permission.ManageExternalStorage) == Permission.Granted)
                    return PermissionStatus.Granted;

                var intent = new Intent(Android.Provider.Settings.ActionManageAppAllFilesAccessPermission);
                intent.AddCategory(Android.Content.Intent.CategoryDefault);
                intent.SetData(Android.Net.Uri.Parse($"package:{Platform.AppContext.PackageName}"));
                Platform.CurrentActivity.StartActivity(intent);

                return await CheckStatusAsync();
            }
        }
        public override void EnsureDeclared()
        {
            var packageName = Platform.AppContext.PackageName;
            var info = Platform.AppContext.PackageManager.GetPackageInfo(packageName, PackageInfoFlags.Permissions);

            var declaredPermissions = info?.RequestedPermissions?.ToList() ?? new List<string>();

            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ReadExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ReadExternalStorage} in AndroidManifest.xml");
                if (!declaredPermissions.Contains(Android.Manifest.Permission.WriteExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.WriteExternalStorage} in AndroidManifest.xml");
            }
            else
            {
                if (!declaredPermissions.Contains(Android.Manifest.Permission.ManageExternalStorage))
                    throw new PermissionException($"You must declare the permission {Android.Manifest.Permission.ManageExternalStorage} in AndroidManifest.xml");
            }
        }

        public override bool ShouldShowRationale()
        {
            if (Build.VERSION.SdkInt < BuildVersionCodes.R)
            {
                return Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.ReadExternalStorage)
                       || Platform.CurrentActivity.ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            }
            else
            {
                return false;
            }
        }

    }

Which is the Platform library?

the one from Xamarin.Essentials

using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Essentials;
using Android.OS;
using Android.Content.PM;
using Android.App;
using Android.Content;
Rishi2611 commented 10 months ago

Does any one found solution for this, I am getting same error?

WanftMoon commented 10 months ago

Does any one found solution for this, I am getting same error?

Yeah, if you look at the previous messages, the solution is more or less:

  1. add the new permissions in your manifest
  2. extend the base permissions and invoke them
  3. look at the workarounds, mostly this one (you might not need it, but i needed in my case):

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
    {
            if (Xamarin.Essentials.DeviceInfo.Version.Major >= 13 && (permissions.Where(p => p.Equals("android.permission.WRITE_EXTERNAL_STORAGE")).Any() || permissions.Where(p => p.Equals("android.permission.READ_EXTERNAL_STORAGE")).Any()))
            {
                var wIdx = Array.IndexOf(permissions, "android.permission.WRITE_EXTERNAL_STORAGE");
                var rIdx = Array.IndexOf(permissions, "android.permission.READ_EXTERNAL_STORAGE");
    
                if (wIdx != -1 && wIdx < permissions.Length) grantResults[wIdx] = Permission.Granted;
                if (rIdx != -1 && rIdx < permissions.Length) grantResults[rIdx] = Permission.Granted;
            }
    
            Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            PermissionsImplementation.Current.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    } 
juanes030 commented 10 months ago

Can someone help me solve the storage permission error, create a test app and I need to know what the code would be to request permission in Android 13. My mainactivity is

using Android.App; using Android.Content.PM; using Android.Runtime; using Android.OS;

namespace MvvmPrueba.Droid { [Activity(Label = "MvvmPrueba", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )] public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity { protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState);

        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App());
    }
    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
    {
        Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

}

zerokewl88 commented 10 months ago

Scroll up.. there are examples there. look @ https://github.com/xamarin/Essentials/issues/2041#issuecomment-1790605848

juanes030 commented 10 months ago

already add the following permissions

<use-permission android:name="android.permission.READ_MEDIA_AUDIO"/>

What would be the permission to write to the storage?

juanes030 commented 10 months ago

already add the following permissions

<use-permission android:name="android.permission.READ_MEDIA_AUDIO"/>

What would be the permission to write to the storage?

WanftMoon commented 10 months ago

already add the following permissions What would be the permission to write to the storage?

@juanes030 From what I understood, from android 10+ you don't need permissions to access your own files (https://developer.android.com/training/data-storage/shared/media#storage-permission) But you need permissions to read files created by other apps. (https://developer.android.com/about/versions/13/behavior-changes-13#granular-media-permissions(url))

with that said, i would recommend that you use at least these

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:minSdkVersion="33" android:name="android.permission.READ_MEDIA_VIDEO"/>
juanes030 commented 10 months ago

I need to archivate from xamarin the permission called file ando multimedia content, like this in the attached image Screenshot_20231124_112120_Permission controller

raver99 commented 10 months ago

I think this has been fixed by #2073.

Worked for me by updating to Xamarin Essentials 1.7.5

softlion commented 9 months ago

I found an easy solution.

You don't need anything from above:

  1. Update to Xamarin Essentials 1.8.0 (latest version)
  2. Add the "Xamarin Essentials" nuget to the xamarin Android project.

Fixed. Now the file picker behaves as expected !
No other change.

Why ? Dunno why an older version of xamarin essentials is used by the android project (even when built by appcenter !). I verified and it's 1.8.0 everywhere.

activa commented 9 months ago

I found an easy solution.

You don't need anything from above:

  1. Update to Xamarin Essentials 1.8.0 (latest version)
  2. Add the "Xamarin Essentials" nuget to the xamarin Android project.

Fixed. Now the file picker behaves as expected ! No other change.

Why ? Dunno why an older version of xamarin essentials is used by the android project (even when built by appcenter !). I verified and it's 1.8.0 everywhere.

Xamarin Essentials 1.8 alone definitely doesn't fix the problem. You need one of the workarounds described earlier in this issue. If your app targets SDK 33 or higher, the app needs to request the READ_MEDIA_IMAGES permission.