The Android Permission class needs serious improvements

This class: Unity - Scripting API: Permission needs some improvements to be useful.

1: We need callbacks for the user answer when calling RequestUserPermission:

2: We need a way to check when the user has clicked the “Do not ask me again” option on the system permission dialog, even better if it also comes as a callback from RequestUserPermission.

From Unity’s own documentation:Unity - Manual: Request runtime permissions

“Note that if the user previously checked the “Do not ask me again” option on the system permission dialog, RequestUserPermission() does not open the system dialog.”

However the class offers no way to tell what you must do, the RequestUserPermission has no return and there’s no way to tell if it showed or not, it’s useless in this current state. We will never be able to tell if the dialog appeared and if the user denied it, there’s no way to tell if we should direct the user to the settings screen to enable the permission…

Hi,

thank you for your feedback, I logged your request internally.

also requesting a permission and checking whether it was granted immediatly after doesn’t work for me:

private bool CheckAndroidMicrophonePermission() {
            // check for permission state
            if (Permission.HasUserAuthorizedPermission(Permission.Microphone)) {
                return true;
            }

            // not yet granted => request permission
            Permission.RequestUserPermission(Permission.Microphone);
            // and return final decission
            return Permission.HasUserAuthorizedPermission(Permission.Microphone);
        }

it doesn’t matter if i return the result of HasUserAuthorizedPermission(…) immediatly (as done above) or if i assign it to a new value, and return it then. however calling this method a second time, the first condition is TRUE, so to me it seems as while in the documentation it is written explicitly that RequestUserPermission should be synchronous, it actually isn’t!

my guess is this is caused maybe by my method being called from within an event triggered from a flutter application where unity is embedded

This doesn’t work, as Permission.RequestUserPermission(Permission.Microphone); doesn’t halt your code. For now use a Coroutine for example or do magic with OnApplicationFocus, as the app gains focus when accepting/dismissing the dialog

There is also GitHub - yasirkula/UnityAndroidRuntimePermissions: A native Unity plugin to handle runtime permissions on Android M+ which supposedly has a blocking call but it doesn’t work for me. Maybe needs a small fix. But it illustrates the need. Unity should include something like this or better directly a callback. It’s just another one of those this-is-not-a-professional-product moments I keep having lately…

+1 @Tomas1856 Any news on this issue?

We desperately need to know if “Do not ask again” has ben checked by the user in order to give instructions to explain how to enable permission from settings.

It’s a mandatory Google requirement to be considered in any featuring on Google Play. Please address this issue ASAP, as all Unity games are lacking this feature at this time.

Thank you.

Sadly, no new news.

I’ll try to look in to this. But in any case, since this is not a regression, any improvement applied will only appear in upcoming Unity versions, since 2020.1 and below no longer accepts improvements only bug fixes

The “unofficial” plugin by a Unity a employee does do this: https://github.com/Over17/UnityAndroidPermissions

Why the “official” built in support doesn’t have feature parity is a mystery to me.

We had a talk inside

Apparently there was a plan to have cross platform API for Permissions, which never happened. Since the status of cross platform API is still unknown we’ll move the plugin code into core Unity for UnityEngine.Android.Permissions class.

Thank you for bringing this to our attention. Sorry for the inconvenience.

The new API will look like this:

public class RequestPermissionScript : MonoBehaviour
{
    internal void PermissionCallbacks_PermissionDeniedAndDontAskAgain(string permissionName)
    {
        Debug.Log($"{permissionName} PermissionDeniedAndDontAskAgain");
    }

    internal void PermissionCallbacks_PermissionGranted(string permissionName)
    {
        Debug.Log($"{permissionName} PermissionCallbacks_PermissionGranted");
    }

    internal void PermissionCallbacks_PermissionDenied(string permissionName)
    {
        Debug.Log($"{permissionName} PermissionCallbacks_PermissionDenied");
    }

    void Start()
    {
        if (Permission.HasUserAuthorizedPermission(Permission.Microphone))
        {
            // The user authorized use of the microphone.
        }
        else
        {
            bool useCallbacks = false;
            if (!useCallbacks)
            {
                // We do not have permission to use the microphone.
                // Ask for permission or proceed without the functionality enabled.
                Permission.RequestUserPermission(Permission.Microphone);
            }
            else
            {
                var callbacks = new PermissionCallbacks();
                callbacks.PermissionDenied += PermissionCallbacks_PermissionDenied;
                callbacks.PermissionGranted += PermissionCallbacks_PermissionGranted;
                callbacks.PermissionDeniedAndDontAskAgain += PermissionCallbacks_PermissionDeniedAndDontAskAgain;
                Permission.RequestUserPermission(Permission.Microphone, callbacks);
            }

        }
    }
}

Thanks for letting us know! That will be very useful, especially that iOS and Android don’t handle the flow the same way - I think Android is all done “instantly” whereas on iOS we must use a yield in a coroutine, which makes the implementation more complicated than the above.

Can’t wait for this to be added :wink:

Thanks,

While the docs make it seem like it’s done instantly, it isn’t. I’m waiting for OnApplicationPause( false ) to know when we return from the permission dialog. It’d be great if they both used the same technique (like the callback function design Tomas posted, which I think is more intuitive to a yield for developers coming from other platforms).

The whole permissions management thing is a huge mess. Now there’s even Application.RequestUserAuthorization, but this only works on iOS. And it appears that its counterpart Application.HasUserAuthorization can only be called after Application.RequestUserAuthorization, not before like on Android. Also, the docs state that HasUserAuthorization is for “Web Player” (and that Web Player is deprecated), whereas RequestUserAuthorization explicitly suggests using it on iOS. WTF?

Hi there, I still can’t see a possibility to know when the user has selected “Deny and don’t ask again” so we know not to ask again. I am checking the permissions at a preloader scene, but now in that scenario it won’t go to the game scene since I have no way to tell Unity that it needs to ignore the permission since the user asked to not be bothered anymore.

I’ve handled a basic deny and ofcourse the grant. But “Deny and don’t ask again” creates is a problem until the user has granted permissions manually.

Thanks for the help!

This was a while ago, but I think I simply used Player Prefs to store if “Never ask again” was requested. That’s fine for us because permission remains device-specific so we will ask again if installed on another device and if saved data is completely deleted (acceptable as well, I think).

At the time there was no other way; I’m not sure if there is now a built-in way to know that distinction of “Deny” VS “Deny and don’t ask again”.

Hope this helps,
Charles

Ah wait, I checked again, I think:

  • Initially the permission status is like “Undefined” or something. If the user has Denied in the past, it’s no longer “Undefined” but “Denied”.

In that case, I just no longer ask again, by checking if it’s already explicitly denied for this app in the past:

AndroidRuntimePermissions.Permission currentCamPermissionState = AndroidRuntimePermissions.CheckPermission(UnityEngine.Android.Permission.Camera);
wasAlreadyCompletelyDenied = currentCamPermissionState == AndroidRuntimePermissions.Permission.Denied;

If it wasAlreadyCompletelyDenied is true, then I simply don’t ask for the permission. Not sure if it’s ideal for you, but worked for my case :wink:

Apologies, there is a chance I finally switched to using another plugin instead of the Unity system, due to that exact limitation, so maybe what I showed above doesn’t work for Unity’s permission system.

It would be a surprise to me that this would still be impossible to do in Unity’s permissions for Android.

I think I used this instead:

I didn’t think of saving it in PlayerPrefs. So simple I couldn’t see it haha. Thanks. As of Unity 2021.2f1 the options for the permissions are still the same as discussed here originally. I am using it to ask location permission for Mapbox so a restart isn’t necessary. (Otherwise the location permission would be asked when loading the map and when given the map already tried to initialize so it won’t load again until you reboot the app) I want to do the same thing for IOS, but I can’t find anything about that. If you know something, it would be helpfull. Thanks! :slight_smile: