Unity + Android: как запросить несколько разрешений? - PullRequest
1 голос
/ 06 ноября 2019

Я работаю над проектом Unity (версия 2019.1.11) с целевой платформой Android, и мне нужно иметь следующие разрешения для работы моего приложения:

android.permission.CAMERA
android.permission.RECORD_AUDIO
android.permission.WRITE_EXTERNAL_STORAGE
android.permission.READ_PHONE_STATE

В документации Unity указывается это способ запросить разрешения для Android. Моя цель - провести начальную проверку всех необходимых разрешений во время запуска приложения, например:

private void AskPermissions()
{
#if UNITY_ANDROID
    if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
    {
        Permission.RequestUserPermission(Permission.Microphone);
    }
    if (!Permission.HasUserAuthorizedPermission(Permission.Camera))
    {
        Permission.RequestUserPermission(Permission.Camera);
    }
    if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
    {
        Permission.RequestUserPermission(Permission.ExternalStorageWrite);
    }
    if (!Permission.HasUserAuthorizedPermission("android.permission.READ_PHONE_STATE"))
    {
        Permission.RequestUserPermission("android.permission.READ_PHONE_STATE");
    }
#endif
}

Однако это не работает: приложение отображает диалоговое окно только для первого разрешения, котороене авторизован и не для тех, кто не авторизован и которые проверяются впоследствии.

Как я могу гарантировать, что всегда проверяются все разрешения?

Ответы [ 3 ]

1 голос
/ 06 ноября 2019

Кажется, что Permission.RequestUserPermission работает как-то асинхронно и не будет отображать диалоговое окно, когда оно уже показано - и поэтому просто пропускает все остальные разрешения после нахождения первого не авторизованного.
Я мог бы обойти проблему следующим образом:

private IEnumerator AskForPermissions()
{
#if UNITY_ANDROID
    List<bool> permissions = new List<bool>() { false, false, false, false };
    List<bool> permissionsAsked = new List<bool>() { false, false, false, false };
    List<Action> actions = new List<Action>()
    {
        new Action(() => {
            permissions[0] = Permission.HasUserAuthorizedPermission(Permission.Microphone);
            if (!permissions[0] && !permissionsAsked[0])
            {
                Permission.RequestUserPermission(Permission.Microphone);
                permissionsAsked[0] = true;
                return;
            }
        }),
        new Action(() => {
            permissions[1] = Permission.HasUserAuthorizedPermission(Permission.Camera);
            if (!permissions[1] && !permissionsAsked[1])
            {
                Permission.RequestUserPermission(Permission.Camera);
                permissionsAsked[1] = true;
                return;
            }
        }),
        new Action(() => {
            permissions[2] = Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite);
            if (!permissions[2] && !permissionsAsked[2])
            {
                Permission.RequestUserPermission(Permission.ExternalStorageWrite);
                permissionsAsked[2] = true;
                return;
            }
        }),
        new Action(() => {
            permissions[3] = ermission.HasUserAuthorizedPermission("android.permission.READ_PHONE_STATE");
            if (!permissions[3] && !permissionsAsked[3])
            {
                Permission.RequestUserPermission("android.permission.READ_PHONE_STATE");
                permissionsAsked[3] = true;
                return;
            }
        })
    };
    for(int i = 0; i < permissionsAsked.Count; )
    {
        actions[i].Invoke();
        if(permissions[i])
        {
            ++i;
        }
        yield return new WaitForEndOfFrame();
    }
#endif
}
0 голосов
/ 07 ноября 2019

Разрешения среды выполнения Android широко известны как проблемные, в вашем случае проблема в том, что панель пользовательского интерфейса запроса разрешений Android захватывает передний план на первом Permission.RequestUserPermission, приостанавливая работу вашего приложения и предотвращая выполнение следующего кода

Самое чистое решение, которое я до сих пор рассматривал для обработки прав доступа, состоит в том, чтобы запустить бесконечный цикл в сопрограмму, которая жадно проверяет необходимые вам разрешения и предлагает пользователю предоставить каждое из них, если оно не

ЭтоНа первый взгляд может показаться уродливым, но учтите, что при запросе разрешения цикл прерывается по той же причине, по которой ваш код не работает: потому что панель пользовательского интерфейса запроса разрешения захватывает передний план, и ваше приложение приостанавливается, когда фокусПосле возвращения ваш цикл снова начинает проверять отсутствующие разрешения, плюс вы можете замедлить ваш цикл с возвращением доходности, новым WaitForSeconds (0.2f)

Это код с некоторым оформлением, чтобы уведомить вас. Так как он не может работать без предоставления всех необходимых разрешений:

private bool _locationPermissionAsked;
private bool _microphonePermissionAsked;
private bool _cameraPermissionAsked;
private bool _storagePermissionAsked;

private void Start()
{
#if UNITY_ANDROID && !UNITY_EDITOR
    StartCoroutine(RequestPermissionsRoutine());        
#else
    /***** Ready to run you app *****/
#endif
}

private IEnumerator RequestPermissionsRoutine()
{
    yield return new WaitForEndOfFrame();
    while (true)
    {
        // For each permission you need, build a block like the following, it could 
        // have been done dynamically but given the small ammount of possible options 
        // I preferred to keep it extended
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) && !_locationPermissionAsked)
        {
            // This flag keeps track of the user choice against the permission panel
            //
            // if he choose to NOT grant the permission we skip the permission request
            // because we are gonna notify him that he needs to grant the permission later 
            // using a message in our App
            _locationPermissionAsked = true; 

            // You can even ask permissions using android literal definition instead of Unity's Permission.FineLocation
            yield return Permission.RequestPermission("android.permission.ACCESS_FINE_LOCATION").WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone) && !_microphonePermissionAsked)
        {
            _microphonePermissionAsked = true;
            yield return Permission.RequestPermission(Permission.Microphone).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Camera) && !_cameraPermissionAsked)
        {
            _cameraPermissionAsked = true;
            yield return Permission.RequestPermission(Permission.Camera).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite) && !_storagePermissionAsked)
        {
            _storagePermissionAsked = true;
            yield return Permission.RequestPermission(Permission.ExternalStorageWrite);
            continue;
        }

        // This is the part where we check if all the permissions were granted
        // and allow the user to run the App ( else condition )
        // or prompt him to grant the permissions he denied
        //
        // Note that this code is never reached before each permission have been asked 
        // once, because of the "continue;"s used before
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) ||
            !Permission.HasUserAuthorizedPermission(Permission.Microphone) ||
            !Permission.HasUserAuthorizedPermission(Permission.Camera) ||
            !Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
        {
            if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
            {
                /***** Tell the user to grant FineLocation Permission *****/
            }

            if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
            {
                /***** Tell the user to grant Microphone Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.Camera))
            {
                /***** Tell the user to grant Camera Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
            {
                /***** Tell the user to grant ExternalStorageWrite Permission *****/
            }

            /***** This is where all permissions have been asked once  *****/
            /***** and one or more were NOT granted,                   *****/
            /***** you can write the code to handle this fallback here *****/
        }
        else
        {
            // I like to give some time to the Android looper before running my App, just to be safe
            yield return new WaitForSeconds(1f); 

            /***** Ready to run you App *****/
        }

        // Slow down the loop by a little bit, not strictly needed but not harmful either
        yield return new WaitForSeconds(0.2f);
    }
}

Это работает даже в тех случаях, когда, например, пользователь отказывает в одном разрешении и затем принудительно убивает ваше приложение, или какое-то другое приложение захватываетнеожиданно на переднем плане и другие рискованные случаи, с которыми я сталкивался до сих пор

Я добавлю небольшой плюс: если вы используете Google ARCore, имейте в виду, что он переопределяет оригинальный механизм запроса разрешений Unity (https://github.com/google-ar/arcore-unity-sdk/issues/151)поощряя разработчиков использовать свой собственный GoogleARCore.AndroidPermissionsManager вместо UnityEngine.Android.Permission, в результате чего UnityEngine.Android.Permission не будет работать, вы все равно можете использовать этот скрипт, но вам нужно будет заменить все «Permission.RequestUserPermission» на«AndroidPermissionsManager.RequestUserPermission» (вам не нужно заменять «Permission.HasUserAuthorizedPermission», они все еще работают), или даже лучше, я могу сделать это для вас:

private IEnumerator RequestPermissionsRoutine()
{
    yield return new WaitForEndOfFrame();
    while (true)
    {
        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) && !_locationPermissionAsked)
        {
            _locationPermissionAsked = true; 
            yield return AndroidPermissionsManager.RequestPermission("android.permission.ACCESS_FINE_LOCATION").WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Microphone) && !_microphonePermissionAsked)
        {
            _microphonePermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.Microphone).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.Camera) && !_cameraPermissionAsked)
        {
            _cameraPermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.Camera).WaitForCompletion();
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite) && !_storagePermissionAsked)
        {
            _storagePermissionAsked = true;
            yield return AndroidPermissionsManager.RequestPermission(Permission.ExternalStorageWrite);
            continue;
        }

        if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation) ||
            !Permission.HasUserAuthorizedPermission(Permission.Microphone) ||
            !Permission.HasUserAuthorizedPermission(Permission.Camera) ||
            !Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
        {
            if (!Permission.HasUserAuthorizedPermission(Permission.FineLocation))
            {
                /***** Tell the user to grant FineLocation Permission *****/
            }

            if (!Permission.HasUserAuthorizedPermission(Permission.Microphone))
            {
                /***** Tell the user to grant Microphone Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.Camera))
            {
                /***** Tell the user to grant Camera Permission *****/
            }

            if (Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite))
            {
                /***** Tell the user to grant ExternalStorageWrite Permission *****/
            }
            /***** This is where all permissions have been asked once  *****/
            /***** and one or more were NOT granted,                   *****/
            /***** you can write the code to handle this fallback here *****/
        }
        else
        {
            yield return new WaitForSeconds(1f); 

            /***** Ready to run you App *****/
        }

        yield return new WaitForSeconds(0.2f);
    }
}
0 голосов
/ 06 ноября 2019

Я столкнулся с той же проблемой в проекте, над которым я работаю. Легкое решение - использовать сопрограмму для запроса разрешений и возврата в течение нескольких секунд между каждым разрешением. Или просто попросите разрешения в разных местах приложения. Например, запрашивайте только разрешение камеры, когда пользователь собирается ее использовать, или запись на внешнее хранилище, когда он собирается получить доступ. Поскольку это конфиденциальные разрешения, пользователю лучше заранее знать причину, по которой его просят предоставить эти разрешения.

IEnumerator Start() {
        // Ask for camera permission
        if(!Permission.HasUserAuthorizedPermission(Permission.Camera)) {
            Permission.RequestUserPermission(Permission.Camera);
        }
        yield return new WaitForSeconds(2.5f);
        // Ask for external storage permission
        if(!Permission.HasUserAuthorizedPermission(Permission.ExternalStorageWrite)) {
            Permission.RequestUserPermission(Permission.ExternalStorageWrite);
        }
}

Приведенный выше код работает. Вы можете настроить значение времени ожидания в секундах и добавить необходимые разрешения. Обратите внимание, что я спрашиваю разрешения, только если они еще не были предоставлены.

Приветствия!

...