Android Marshmallow: как программно разрешить права выполнения?

Я разрабатываю приложение, для которого требуются некоторые системные разрешения, однако они больше не предоставляются автоматически во время установки на Android Marshmallow.

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

Как я могу этого добиться? Есть ли способ сделать это в Marshmallow и более поздних версиях?




Ответы (3)


Для Marshmallow или более поздних версий разрешения не предоставляются во время установки и должны запрашиваться при необходимости во время выполнения (если они не были предоставлены ранее).

Для этого вам нужно запустить ActivityCompat.requestPermissions(), чтобы открыть диалоговое окно системных разрешений в вашей деятельности в то время, когда пользователь выполняет действие, требующее дополнительных системных разрешений.

Примером этого для разрешения WRITE_EXTERNAL_STORAGE может быть:

ActivityCompat.requestPermissions(
    this, 
    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
    WRITE_EXTERNAL_STORAGE_REQUEST_CODE
);

Примечание: WRITE_EXTERNAL_STORAGE_REQUEST_CODE — это произвольная целочисленная константа, которую вы должны определить в другом месте.

Разрешения, которые вы запрашиваете, также должны быть объявлены в вашем AndroidManifest.xml. В этом примере объявление будет таким:

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

Чтобы обработать ответ диалогового окна системных разрешений, вам также необходимо реализовать onRequestPermissionsResult() в своей деятельности. Для этого примера код будет похож на

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    if (grantResults.length == 0 || grantResults[0] == PackageManager.PERMISSION_DENIED) {
        return; //permission not granted, could also optionally log an error
    }
    if (requestCode == WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {
        //Do whatever you needed the write permissions for            
    }
}

Если вы автоматизируете свое приложение с помощью Espresso, UIAutomator и/или другой фреймворк для тестирования пользовательского интерфейса, который вам понадобится ожидать и щелкнуть системный диалог во время теста, что можно выполнить с помощью следующего тестового кода:

private void allowPermissionsIfNeeded()  {
    if (Build.VERSION.SDK_INT >= 23) {
        UiObject allowPermissions = mDevice.findObject(new UiSelector().text("Allow"));
        if (allowPermissions.exists()) {
            try {
                allowPermissions.click();
            } catch (UiObjectNotFoundException e) {
                Timber.e(e, "There is no permissions dialog to interact with ");
            }
        }
    }
}

Более полное объяснение тестирования разрешений пользовательского интерфейса системы доступно здесь.

person Nathan Dunn    schedule 23.12.2016
comment
Спасибо за подробное объяснение, хотя мне нужна только последняя часть после «Эспрессо». Кстати, я не автоматизирую свое приложение через «Эспрессо», я просто нажимаю кнопки и контролирую поток приложения из приложения. Можно ли сделать это, не используя что-то вроде «Эспрессо»? - person KayPee; 23.12.2016
comment
Не за что :) Что именно вы пытаетесь автоматизировать и с какой целью? Как вы это делаете в настоящее время? Немного неясно, чего вы пытаетесь достичь (или что вы имеете в виду, нажимая кнопки в приложении) из описания вопроса. - person Nathan Dunn; 23.12.2016
comment
Это просто означает, что всякий раз, когда я хочу нажать кнопку 1, я выполняю кнопку 1.performClick(). Итак, есть ли способ нажать «Разрешить» в системном диалоговом окне всякий раз, когда мое приложение запрашивает разрешение? - person KayPee; 23.12.2016
comment
Проще говоря, я хочу предоставлять разрешения внутри, без какого-либо взаимодействия с пользователем. - person KayPee; 23.12.2016
comment
Это противоречит цели запроса разрешений у пользователя и представляет собой уязвимость в системе безопасности — единственный способ автоматизировать такое взаимодействие — управлять устройством вне самого приложения (обычно с использованием какой-либо среды тестирования Instrumentation/UI). - person Nathan Dunn; 23.12.2016
comment
Кроме того, с точки зрения нажатия кнопки программно, лучшим способом было бы иметь служебный метод, который вызывается в методе onClick кнопки (который вы можете вызвать в другом месте программы). Запуск функциональности путем имитации нажатия кнопки немного запутан (не говоря уже о сложности эффективного модульного тестирования). Вы можете рассмотреть архитектуру MVP — ваша активность более или менее должна обрабатывать только механику интерфейса, разделяя бизнес-логику на отдельный класс Presenter. - person Nathan Dunn; 23.12.2016
comment
Почему вы хотите вызвать нажатие кнопки? Можете ли вы объяснить, чего вы пытаетесь достичь в целом, немного подробнее? - person Nathan Dunn; 23.12.2016
comment
Да, это определенно сведет на нет цель разрешений во время выполнения. И типичный способ сделать это — использовать какую-то среду тестирования Instrumentation/UI. Но я этого не знаю, поэтому решил использовать внутренние события кликов. Более того, мне нужно совсем немного кликов, чтобы достичь своей цели. Итак, я выбрал это. Но я застрял в этой части (разрешение разрешений). Я думаю, что тогда мне придется перейти на среду тестирования Instrumentation/UI. - person KayPee; 23.12.2016
comment
Я еще не совсем уверен, какова ваша цель - если вы наметите, что дальше могут быть и другие варианты. Кроме того, если вы довольны ответом, пожалуйста, примите его и проголосуйте. Спасибо. - person Nathan Dunn; 23.12.2016

Я обнаружил, что более простой способ автоматизировать принятие разрешений без использования UIAutomator или эспрессо в сценарии CI — просто предварительно установить apk через adb, используя:

adb install -g {my_apk_file}

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

person Joao Neto    schedule 31.08.2017
comment
Это помогло мне в режиме SuperUser - person Dhruv Kaushal; 18.12.2017

Если вашему приложению требуется опасное разрешение, вы должны проверять, есть ли у вас это разрешение каждый раз, когда вы выполняете операцию, требующую этого разрешения.

Вам необходимо выполнить следующий шаг. 1. Проверьте разрешения

// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity,
        Manifest.permission.WRITE_CALENDAR);

Запросить разрешения

// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
                Manifest.permission.READ_CONTACTS)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
            Manifest.permission.READ_CONTACTS)) {

        // Show an explanation to the user *asynchronously* -- don't block
        // this thread waiting for the user's response! After the user
        // sees the explanation, try again to request the permission.

    } else {

        // No explanation needed, we can request the permission.

        ActivityCompat.requestPermissions(thisActivity,
                new String[]{Manifest.permission.READ_CONTACTS},
                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

        // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
        // app-defined int constant. The callback method gets the
        // result of the request.
    }
}

Обработка ответа на запрос разрешений

@Override
public void onRequestPermissionsResult(int requestCode,
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                // permission was granted, yay! Do the
                // contacts-related task you need to do.

            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.
            }
            return;
        }

        // other 'case' lines to check for other
        // permissions this app might request
    }
}
person Surya Prakash Kushawah    schedule 23.12.2016
comment
Спасибо @Surya, я уже добавил приведенный выше код в свое приложение, и оно отлично работает. Дело в том, что я хочу предоставить разрешения из приложения. Например, всякий раз, когда появляется диалоговое окно разрешений, оно просто автоматически нажимает «Разрешить» или что-то в этом роде. - person KayPee; 23.12.2016