Android M - перевірити дозвіл на виконання - як визначити, чи користувач перевірив "Ніколи більше не запитувати"?


307

Відповідно до цього: http://developer.android.com/preview/features/runtime-permissions.html#coding додаток може перевірити наявність дозволів на виконання та запитувати дозволи, якщо він ще не був наданий. Потім з’явиться наступне діалогове вікно:

введіть тут опис зображення

Якщо користувач відхиляє важливий дозвіл, додаток imo повинен відображати пояснення, для чого потрібен дозвіл і який вплив має зменшення. У цьому діалоговому вікні є два варіанти:

  1. повторити спробу (дозвіл запитується знову)
  2. відмовити (додаток буде працювати без цього дозволу).

Якщо користувач перевіряє Never ask again, друге діалогове вікно з поясненням не повинно бути показане, особливо якщо користувач вже відмовився раніше. Тепер питання: як моя програма знає, чи перевірив користувач Never ask again? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)не дає мені цієї інформації.

Другим питанням буде: чи планує Google включити в діалогове вікно дозволу користувальницьке повідомлення, яке б пояснило, чому програма потребує дозволу? Таким чином, ніколи не буде другого діалогу, який, безумовно, сприятиме кращому змісту.


9
"чи планує Google включити користувальницьке повідомлення у діалоговому вікні дозволу, яке б пояснювало, чому програма потребує дозволу?" - в презентації Google I | O про систему дозволів на M я, мабуть, згадую, що хтось запитував у запитаннях, і відповідь полягав у тому, що вони думають про це.
CommonsWare

1
Я сам не перевіряв це, але в документації йдеться про Activity.shouldShowRequestPermissionRationale (String): Цей метод повертає істину, якщо програма раніше вимагала цього дозволу, а користувач відхилив запит. Це вказує на те, що ви, ймовірно, повинні пояснити користувачеві, чому вам потрібен дозвіл. Якщо в минулому користувач відхилив запит на дозвіл і вибрав опцію Не запитувати знову в діалоговому вікні системи запитів дозволів, цей метод повертає помилкове значення. Метод також повертає значення false, якщо політика пристрою забороняє додатку мати такий дозвіл.
Фрейд

1
@Fraid: схоже, вони додали це до попереднього перегляду №2 для Android M: developer.android.com/preview/support.html#preview2-notes, і це, мабуть, те, що я шукав. Я не можу перевірити це зараз, але зроблю це на наступному тижні. Якщо він буде робити те, що я сподіваюся, це можна зробити, ви можете опублікувати його як відповідь і отримати деяку репутацію. Тим часом це може допомогти іншим: youtube.com/watch?v=f17qe9vZ8RM
Emanuel Moecklin

приклад небезпечних дозволів та спеціальних дозволів: github.com/henrychuangtw/AndroidRuntimePermission
HenryChuang

1
@ Алекс важче для розробників, це точно, але з точки зору користувача можливість надавати або забороняти конкретні дозволи має сенс. Основна проблема, яку я бачу, полягає в тому, що деталізація дозволів дуже непослідовна, і ви в кінцевому підсумку запитуєте дозволу, який може мати майже нічого спільного з тим, що ви намагаєтесь робити у вашому додатку (наприклад, дозвіл на контакти, коли я хочу підключитися до Google Диск, оскільки для списку облікових записів пристрою для цілей аутентифікації потрібен перелік облікових записів пристрою, а дозвіл облікового запису є частиною дозвольної групи контактів)
Емануель Моеклін

Відповіді:


341

Попередній перегляд розробника 2 вносить деякі зміни до того, як програма вимагає дозволів (див. Також http://developer.android.com/preview/support.html#preview2-notes ).

Перший діалог зараз виглядає приблизно так:

введіть тут опис зображення

Немає поля "Ніколи більше не показувати" (на відміну від попереднього перегляду розробника 1). Якщо користувач відмовляє у дозволі і якщо дозвіл є важливим для програми, він може відкрити ще одне діалогове вікно, щоб пояснити причину, за якою програма вимагає цього дозволу, наприклад, наприклад:

введіть тут опис зображення

Якщо користувач знову відхилиться, додаток слід або закрити, якщо він абсолютно потребує цього дозволу, або продовжувати працювати з обмеженою функціональністю. Якщо користувач переглядає (і вибирає повторну спробу), дозвіл запитується знову. Цього разу підказка виглядає приблизно так:

введіть тут опис зображення

Вдруге з'являється прапорець "Ніколи не запитай більше". Якщо користувач знову відмовить і поставлений прапорець, більше нічого не повинно відбуватися. Поставити прапорець чи ні, можна визначити за допомогою Activity.shouldShowRequestPermissionRationale (String), наприклад так:

if (shouldShowRequestPermissionRationale(Manifest.permission.WRITE_CONTACTS)) {...

Про це говорить документація на Android ( https://developer.android.com/training/permissions/requesting.html ):

Щоб допомогти знайти ситуації, коли потрібно надати додаткове пояснення, система надає метод Activity.shouldShowRequestPermissionRationale (String). Цей метод повертає істину, якщо програма раніше вимагала цього дозволу і користувач відмовив у запиті. Це вказує на те, що ви, ймовірно, повинні пояснити користувачеві, чому вам потрібен дозвіл.

Якщо в минулому користувач відхилив запит на дозвіл і вибрав опцію Не запитувати знову в діалоговому вікні системи запитів дозволів, цей метод повертає помилкове значення. Метод також повертає значення false, якщо політика пристрою забороняє додатку мати такий дозвіл.

Щоб знати, якщо користувач відмовив "ніколи не запитувати знову", ви можете знову перевірити метод shouldShowRequestPermissionRationale у своєму onRequestPermissionsResult, коли користувач не надав дозволу.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    if (requestCode == REQUEST_PERMISSION) {
        // for each permission check if the user granted/denied them
        // you may want to group the rationale in a single dialog,
        // this is just an example
        for (int i = 0, len = permissions.length; i < len; i++) {
            String permission = permissions[i];
            if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
            // user rejected the permission
                boolean showRationale = shouldShowRequestPermissionRationale( permission );
                if (! showRationale) {
                    // user also CHECKED "never ask again"
                    // you can either enable some fall back,
                    // disable features of your app
                    // or open another dialog explaining
                    // again the permission and directing to
                    // the app setting
                } else if (Manifest.permission.WRITE_CONTACTS.equals(permission)) {
                    showRationale(permission, R.string.permission_denied_contacts);
                    // user did NOT check "never ask again"
                    // this is a good place to explain the user
                    // why you need the permission and ask if he wants
                    // to accept it (the rationale)
                } else if ( /* possibly check more permissions...*/ ) {
                }
            }
        }
    }
}

Ви можете відкрити налаштування програми за допомогою цього коду:

Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivityForResult(intent, REQUEST_PERMISSION_SETTING);

Немає можливості направити користувача безпосередньо на сторінку авторизації.


30
Я перевірив значення повернення методу shouldShowRequestPermissionRationale () на хибне для перевірки, чи вибрав користувач "Ніколи не запитувати більше". Але я також вперше отримую його як неправдиве значення, коли запитую дозволу. Тому я не в змозі розмежувати, чи користувач вибрав прапорець "Ніколи більше не запитувати" чи ні. Підкажіть будь ласка ??
Сагар Трехан

32
Згідно з моїм розумінням, метод shouldShowRationalePermissionRationale () повертає помилку у трьох випадках: 1. Якщо ми закликаємо цей метод вперше, перш ніж запитати дозвіл. 2. Якщо користувач вибере "Не запитувати знову" і відмовить у дозволі. 3. Якщо політика пристрою забороняє додатку мати такий дозвіл
Сагар Трехан

24
Все добре ... але нам, розробникам, дійсно потрібно знати, чи сказав користувач "ніколи більше не питати" чи ні. У мене є приємна кнопка для доступу до функції. Перший раз, коли користувач натискає: повинен запитати обґрунтування? ні, запитайте дозволу. Користувач заперечує. Користувач знову натискає кнопку: обгрунтування? так! Покажіть обґрунтування, скажіть користувач Добре, потім заперечуйте і ніколи більше не запитайте (добре, він ідіот, але користувачі часто є). Пізніше користувач знову натисне кнопку, обґрунтування? ні, запитайте дозволу, з користувачем нічого не відбувається. Мені справді потрібен спосіб сказати користувачеві: ей, якщо ти хочеш цю функцію, перейди до налаштування програми та дай дозвіл.
Даніеле Сегато

4
Чудово @EmanuelMoecklin це краще, ніж Google Документація зараз: D
Daniele Segato

4
onRequestPermissionsResult не буде викликаний, якщо ви не попросите дозволу. Оскільки при першому запиті дозволу не встановлено прапорець "Ніколи не запитуй знову", у випадку, якщо дозволити дозвіл повернеться, TrueShowRequestPermissionRationale поверне значення True (дозвіл запитується, але ніколи не запитувати знову). Отже, обґрунтування завжди показується в перший раз, коли користувач відхиляє дозвіл, але після цього, лише якщо прапорець не встановлений.
Емануель Моеклін

95

Ви можете перевірити shouldShowRequestPermissionRationale()свій onRequestPermissionsResult().

shouldShowRequestPermissionRationale https://youtu.be/C8lUdPVSzDk?t=2m23s

Перевірте, чи надано дозвіл чи ні onRequestPermissionsResult(). Якщо ні, то перевірте shouldShowRequestPermissionRationale().

  1. Якщо цей метод повертається, trueто покажіть пояснення, чому саме цей дозвіл потрібен. Потім знову залежно від вибору користувача requestPermissions().
  2. Якщо він повертається, falseто покажіть повідомлення про помилку, що дозвіл не надано, і додаток не може продовжувати роботу далі або певна функція вимкнена.

Нижче наведено зразок коду.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case STORAGE_PERMISSION_REQUEST:
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission was granted :)
                downloadFile();
            } else {
                // permission was not granted
                if (getActivity() == null) {
                    return;
                }
                if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    showStoragePermissionRationale();
                } else {
                    Snackbar snackbar = Snackbar.make(getView(), getResources().getString(R.string.message_no_storage_permission_snackbar), Snackbar.LENGTH_LONG);
                    snackbar.setAction(getResources().getString(R.string.settings), new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            if (getActivity() == null) {
                                return;
                            }
                            Intent intent = new Intent();
                            intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                            Uri uri = Uri.fromParts("package", getActivity().getPackageName(), null);
                            intent.setData(uri);
                            OrderDetailFragment.this.startActivity(intent);
                        }
                    });
                    snackbar.show();
                }
            }
            break;
    }
}

Мабуть, Google Maps робить саме це для дозволу на місцезнаходження.


Дякую за фото та посилання на Youtube. Це більше чи менше відповідає моїй власній відповіді. Слід зазначити, що питання було задано, коли був доступний лише попередній перегляд розробника 1, у якому не було методу shouldShowRequestPermissionRationale.
Емануель Моеклін

Я новачок в android, і я хочу перевершити цей метод onRequestPermissionsResult (). але я отримую помилку, що він повинен реалізувати метод супер типу. ви можете сказати, як це використовувати
Andrain

39

Ось приємний і простий метод перевірити поточний статус дозволу:

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({GRANTED, DENIED, BLOCKED_OR_NEVER_ASKED })
    public @interface PermissionStatus {}

    public static final int GRANTED = 0;
    public static final int DENIED = 1;
    public static final int BLOCKED_OR_NEVER_ASKED = 2;

    @PermissionStatus 
    public static int getPermissionStatus(Activity activity, String androidPermissionName) {
        if(ContextCompat.checkSelfPermission(activity, androidPermissionName) != PackageManager.PERMISSION_GRANTED) {
            if(!ActivityCompat.shouldShowRequestPermissionRationale(activity, androidPermissionName)){
                return BLOCKED_OR_NEVER_ASKED;
            }
            return DENIED;
        }
        return GRANTED;
    }

Caveat: повертає BLOCKED_OR_NEVER_ASKED перший запуск програми, перш ніж користувач прийняв / відмовив дозвіл через запит користувача (на пристроях sdk 23+)

Оновлення:

Зараз у бібліотеці підтримки Android також схожий клас, android.support.v4.content.PermissionCheckerякий містить checkSelfPermission()повернення:

public static final int PERMISSION_GRANTED = 0;
public static final int PERMISSION_DENIED = -1;
public static final int PERMISSION_DENIED_APP_OP = -2;

1
Для першого запуску я зберігаю булеве значення у спільних налаштуваннях.
Саїд Фарівар

5
Це завжди повертається, BLOCKED_OR_NEVER_ASKEDякщо дозвіл ще не запитували.
Сакет

6
так, саме тому його називають "BLOCKED_OR_NEVER_ASKED", також дивіться останнє речення
Патрік Фавре

3
android.content.pmвже визначає PERMISSION_GRANTED = 0і PERMISSION_DENIED = -1. Можливо, набір BLOCKED_OR_NEVER_ASKED = PERMISSION_DENIED - 1чи щось таке?
Саміс

Дивіться відповідь mVck нижче щодо обробки застережень.
Саміс

28

Після того, як користувач позначив "Не запитувати знову", питання не може відображатися знову. Але користувачеві можна пояснити, що він раніше відмовив у дозволі і повинен надати дозвіл у налаштуваннях. І посилайте його на налаштування із таким кодом:

@Override
public void onRequestPermissionsResult(int permsRequestCode, String[] permissions, int[] grantResults) {

    if (grantResults.length > 0
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // now, you have permission go ahead
        // TODO: something

    } else {

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.READ_CALL_LOG)) {
            // now, user has denied permission (but not permanently!)

        } else {

            // now, user has denied permission permanently!

            Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "You have previously declined this permission.\n" +
                "You must approve this permission in \"Permissions\" in the app settings on your device.", Snackbar.LENGTH_LONG).setAction("Settings", new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + BuildConfig.APPLICATION_ID)));

            }
        });
        View snackbarView = snackbar.getView();
        TextView textView = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
        textView.setMaxLines(5);  //Or as much as you need
        snackbar.show();

        }

    }
    return;
}

під час міграції на androidX ви можете замінити android.support.design.R на com.google.android.material.R
Ridha Rezzag

26

Може бути корисним для когось: -

Я помітив, що якщо ми перевіримо прапор shouldShowRequestPermissionRationale () у методі зворотного виклику onRequestPermissionsResult (), він показує лише два стани .

Стан 1: Повернення правдиво: - Кожен раз, коли користувач натискає дозволу заборонити (включаючи перший раз).

Стан 2: Повертає помилку: - якщо користувач вибирає "ніколи більше не запитує".

Посилання детального робочого прикладу


2
Це правильний спосіб виявити, чи не вибрав користувач варіант ніколи більше не запитувати.
Мухаммед Бабар

А, головне тут - ви вирішуєте це в області onRequestPermissionsResult, а не тоді, коли насправді вимагаєте дозволу.
Джошуа Пінтер

26

Ви можете визначити це шляхом перевірки , якщо дозвіл обгрунтування має бути показано всередині onRequestPermissionsResult()методу зворотного виклику. Якщо ви знайдете будь-який дозвіл, щоб більше ніколи не запитувати , ви можете попросити користувачів надати дозволи з налаштувань.

Моя повна реалізація була б як нижче. Він працює як для однократних, так і для декількох запитів дозволу. Скористайтеся наступним способом або безпосередньо використовуйте мою бібліотеку.

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    if(permissions.length == 0){
        return;
    }
    boolean allPermissionsGranted = true;
    if(grantResults.length>0){
        for(int grantResult: grantResults){
            if(grantResult != PackageManager.PERMISSION_GRANTED){
                allPermissionsGranted = false;
                break;
            }
        }
    }
    if(!allPermissionsGranted){
        boolean somePermissionsForeverDenied = false;
        for(String permission: permissions){
            if(ActivityCompat.shouldShowRequestPermissionRationale(this, permission)){
                //denied
                Log.e("denied", permission);
            }else{
                if(ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED){
                    //allowed
                    Log.e("allowed", permission);
                } else{
                    //set to never ask again
                    Log.e("set to never ask again", permission);
                    somePermissionsForeverDenied = true;
                }
            }
        }
        if(somePermissionsForeverDenied){
            final AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);
            alertDialogBuilder.setTitle("Permissions Required")
                    .setMessage("You have forcefully denied some of the required permissions " +
                            "for this action. Please open settings, go to permissions and allow them.")
                    .setPositiveButton("Settings", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
                                    Uri.fromParts("package", getPackageName(), null));
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            startActivity(intent);
                        }
                    })
                    .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                        }
                    })
                    .setCancelable(false)
                    .create()
                    .show();
        }
    } else {
        switch (requestCode) {
            //act according to the request code used while requesting the permission(s).
        }
    }
}

Привіт @nabin моя вимога полягає в тому, що коли я натискаю кнопку завантаження (в якій завантажують pdf-файл), то час перевірити дозвіл на запит дозволено чи заборонено, так як використовувати цей код! Ви можете мені керувати мною
Rucha Bhatt Joshi

привіт @RuchaBhatt Поглянь на мою бібліотеку. github.com/nabinbhandari/Android-Permissions
Nabin Bhandari

15

Якщо ви хочете виявити всі "стани" (вперше відмовлено, просто відмовили, просто відмовили за допомогою "Ніколи не запитувати знову" або назавжди відмовили), ви можете зробити наступне:

Створіть 2 булеви

private boolean beforeClickPermissionRat;
private boolean afterClickPermissionRat;

Встановіть перший, перш ніж запитувати дозвіл:

beforeClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Встановіть другий всередині вашого методу onRequestPermissionsResult:

afterClickPermissionRat = shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE);

Використовуйте наступну "таблицю", щоб зробити все, що вам потрібно в OnRequestPermissionsResult () (після перевірки, що ви все ще не маєте дозволу):

// before after
// FALSE  FALSE  =  Was denied permanently, still denied permanently --> App Settings
// FALSE  TRUE   =  First time deny, not denied permanently yet --> Nothing
// TRUE   FALSE  =  Just been permanently denied --> Changing my caption to "Go to app settings to edit permissions"
// TRUE   TRUE   =  Wasn't denied permanently, still not denied permanently --> Nothing

Немає сенсу перевіряти mustShowRequestPermissionRationale перед тим, як викликати requestPermissions, якщо ви не хочете показати обґрунтування, перш ніж запитувати дозвіл. Показано обґрунтування лише після того, як користувач відмовив у дозволі, схоже, як більшість програм сьогодні справляється з цим.
Емануель Моеклін

2
@EmanuelMoecklin, наскільки я знаю, це єдиний спосіб перевірити, чи він уже відхилений (перевіряючи його до і після, як це пояснено в моїй таблиці істинності) або якщо це вперше заперечують (у моєму випадку я перенаправляю користувача на налаштування програми, якщо його назавжди відхилено)
mVck

1
// TRUE FALSEтакож виникає, коли користувач надає дозвіл після попереднього його відхилення.
Саміс

11

У мене була така ж проблема, і я це зрозумів. Щоб зробити життя набагато простішим, я написав клас util для обробки дозволів виконання.

public class PermissionUtil {
    /*
    * Check if version is marshmallow and above.
    * Used in deciding to ask runtime permission
    * */
    public static boolean shouldAskPermission() {
        return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
    }
private static boolean shouldAskPermission(Context context, String permission){
        if (shouldAskPermission()) {
            int permissionResult = ActivityCompat.checkSelfPermission(context, permission);
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true;
            }
        }
        return false;
    }
public static void checkPermission(Context context, String permission, PermissionAskListener listener){
/*
        * If permission is not granted
        * */
        if (shouldAskPermission(context, permission)){
/*
            * If permission denied previously
            * */
            if (((Activity)context).shouldShowRequestPermissionRationale(permission)) {
                listener.onPermissionPreviouslyDenied();
            } else {
                /*
                * Permission denied or first time requested
                * */
if (PreferencesUtil.isFirstTimeAskingPermission(context, permission)) {
                    PreferencesUtil.firstTimeAskingPermission(context, permission, false);
                    listener.onPermissionAsk();
                } else {
                    /*
                    * Handle the feature without permission or ask user to manually allow permission
                    * */
                    listener.onPermissionDisabled();
                }
            }
        } else {
            listener.onPermissionGranted();
        }
    }
/*
    * Callback on various cases on checking permission
    *
    * 1.  Below M, runtime permission not needed. In that case onPermissionGranted() would be called.
    *     If permission is already granted, onPermissionGranted() would be called.
    *
    * 2.  Above M, if the permission is being asked first time onPermissionAsk() would be called.
    *
    * 3.  Above M, if the permission is previously asked but not granted, onPermissionPreviouslyDenied()
    *     would be called.
    *
    * 4.  Above M, if the permission is disabled by device policy or the user checked "Never ask again"
    *     check box on previous request permission, onPermissionDisabled() would be called.
    * */
    public interface PermissionAskListener {
/*
        * Callback to ask permission
        * */
        void onPermissionAsk();
/*
        * Callback on permission denied
        * */
        void onPermissionPreviouslyDenied();
/*
        * Callback on permission "Never show again" checked and denied
        * */
        void onPermissionDisabled();
/*
        * Callback on permission granted
        * */
        void onPermissionGranted();
    }
}

А методи PreferenceUtil такі.

public static void firstTimeAskingPermission(Context context, String permission, boolean isFirstTime){
SharedPreferences sharedPreference = context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE;
 sharedPreference.edit().putBoolean(permission, isFirstTime).apply();
 }
public static boolean isFirstTimeAskingPermission(Context context, String permission){
return context.getSharedPreferences(PREFS_FILE_NAME, MODE_PRIVATE).getBoolean(permission, true);
}

Тепер все, що вам потрібно, це використовувати метод * checkPermission * з належними аргументами.

Ось приклад,

PermissionUtil.checkPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE,
                    new PermissionUtil.PermissionAskListener() {
                        @Override
                        public void onPermissionAsk() {
                            ActivityCompat.requestPermissions(
                                    thisActivity,
              new String[]{Manifest.permission.READ_CONTACTS},
                            REQUEST_EXTERNAL_STORAGE
                            );
                        }
@Override
                        public void onPermissionPreviouslyDenied() {
                       //show a dialog explaining permission and then request permission
                        }
@Override
                        public void onPermissionDisabled() {
Toast.makeText(context, "Permission Disabled.", Toast.LENGTH_SHORT).show();
                        }
@Override
                        public void onPermissionGranted() {
                            readContacts();
                        }
                    });

як моя програма знає, чи користувач перевірив "Ніколи більше не запитувати"?

Якщо користувач поставив прапорець Ніколи більше не запитувати , ви отримаєте зворотний дзвінок onPermissionDisabled .

Щасливе кодування :)


я повинен тут показати помилку, чи можете ви мені допомогти.
Rucha Bhatt Joshi

я не можу знайти цей метод повиненShowRequestPermissionRationale, можливо, мені не вдалося отримати контекст .. але це добре, я знайшов інше альтернативне рішення .. Дякую за допомогу :)
Rucha Bhatt Joshi

1
Моє ліжко. shouldShowRequestPermissionRationale доступний через діяльність, а не контекст. Я оновив свою відповідь, кинувши контекст на "Активність", перш ніж викликати цей метод. Перевірте :)
muthuraj

1
Це єдиний спосіб обійти перше помилкове значення, яке повертає shouldShowRequestPermissionRationale, зберігаючи перевагу запиту, надісланого користувачеві. Я мав таку ж ідею і знайшов вашу відповідь. Хороша людина з роботи
MatPag

4

Повне пояснення для кожного випадку дозволу

/**
 *    Case 1: User doesn't have permission
 *    Case 2: User has permission
 *
 *    Case 3: User has never seen the permission Dialog
 *    Case 4: User has denied permission once but he din't clicked on "Never Show again" check box
 *    Case 5: User denied the permission and also clicked on the "Never Show again" check box.
 *    Case 6: User has allowed the permission
 *
 */
public void handlePermission() {
    if (ContextCompat.checkSelfPermission(MainActivity.this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {
        // This is Case 1. Now we need to check further if permission was shown before or not

        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

            // This is Case 4.
        } else {
            // This is Case 3. Request for permission here
        }

    } else {
        // This is Case 2. You have permission now you can do anything related to it
    }
}

public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {

    if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // This is Case 2 (Permission is now granted)
    } else {
        // This is Case 1 again as Permission is not granted by user

        //Now further we check if used denied permanently or not
        if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            // case 4 User has denied permission but not permanently

        } else {
            // case 5. Permission denied permanently.
            // You can open Permission setting's page from here now.
        }

    }
}

4

Корисна функція для визначення того, чи не було дозволено довільний дозвіл у запиті (у Котліні):

private fun isPermissionBlockedFromAsking(activity: Activity, permission: String): Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        return ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED
            && !activity.shouldShowRequestPermissionRationale(permission)
            && PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false)
    }
    return false
}

Для використання цього потрібно встановити загальний булінг налаштувань спільно з назвою бажаного дозволу (наприклад android.Manifest.permission.READ_PHONE_STATE), trueколи ви вперше вимагаєте дозволу.


Пояснення:

Build.VERSION.SDK_INT >= Build.VERSION_CODES.M оскільки частина коду може бути запущена лише на рівні API 23+.

ContextCompat.checkSelfPermission(activity, permission) != PackageManager.PERMISSION_GRANTED щоб перевірити, у нас уже немає дозволу.

!activity.shouldShowRequestPermissionRationale(permission)щоб перевірити, чи користувач заборонив програмі запитувати ще раз. Через химерність цієї функції також потрібен наступний рядок.

PreferenceManager.getDefaultSharedPreferences(activity).getBoolean(permission, false) це використовується (разом із встановленням значення true на перший запит на дозвіл) для розмежування станів "Ніколи не запитували" та "Ніколи не запитувати знову", оскільки попередній рядок не повертає цю інформацію.


3

Будь ласка, не кидайте мені каміння за це рішення.

Це працює, але трохи "хакі".

Під час дзвінка requestPermissionsзареєструйте поточний час.

        mAskedPermissionTime = System.currentTimeMillis();

Тоді в onRequestPermissionsResult

якщо результат не надається, ще раз перевірте час.

 if (System.currentTimeMillis() - mAskedPermissionTime < 100)

Оскільки користувач не міг так швидко натиснути кнопку відхилення, ми знаємо, що він вибрав "ніколи не запитувати знову", оскільки зворотний виклик миттєвий.

Використовуйте на свій страх і ризик.


що робити, якщо ми побачимо запитуване діалогове вікно протягом 5 хвилин, а потім відмовити?
сакшам

Тоді в чому це користь, якщо воно не може виконати основну вимогу. Код може бути хак, як прийнятий, якщо він чітко відповідає всім вимогам у будь-якому іншому випадку.
сакшам

Так, це погано. Автоматичні тестери на кшталт цього, можливо, зможуть натиснути швидше, ніж це: developer.android.com/training/testing/crawler
stackzebra

3

Метод shouldShowRequestPermissionRationale () може бути використаний для перевірки того, чи вибрав користувач параметр "ніколи більше не запитував" та відмовив у дозволі. Прикладів коду є достатньо, тому я б швидше пояснив, як його використовувати для такої мети, тому що я думаю, що його назва та його реалізація ускладнюють це, чим воно є насправді.

Як пояснено у запиті дозволів під час виконання , цей метод повертає істину, якщо видно опцію "ніколи не запитувати знову", інакше помилково; Таким чином, він повертає помилку в перший раз, коли з'являється діалогове вікно, потім з другого разу він повертає істину, і лише якщо користувач відмовить у дозволі на вибір параметра, в цей момент він знову поверне помилкове.

Щоб виявити такий випадок, ви можете виявити послідовність false-true-false, або (простіше кажучи), ви можете встановити прапор, який відслідковує початковий час відображення діалогу. Після цього цей метод повертає або істинну, або хибну, де помилковий дозволить виявити, коли вибрано параметр.


2

Я написав стенографічний запит на запит на дозвіл в Android M. Цей код також підтримує зворотну сумісність зі старими версіями Android.

Весь потворний код витягується у Фрагмент, який приєднується та відривається від Діяльності, вимагаючи дозволів. Ви можете використовувати PermissionRequestManagerнаступне:

new PermissionRequestManager()
        // We need a AppCompatActivity here, if you are not using support libraries you will have to slightly change 
        // the PermissionReuqestManager class
        .withActivity(this)

        // List all permissions you need
        .withPermissions(android.Manifest.permission.CALL_PHONE, android.Manifest.permission.READ_CALENDAR)

        // This Runnable is called whenever the request was successfull
        .withSuccessHandler(new Runnable() {
            @Override
            public void run() {
                // Do something with your permissions!
                // This is called after the user has granted all 
                // permissions, we are one a older platform where 
                // the user does not need to grant permissions 
                // manually, or all permissions are already granted

            }
        })

        // Optional, called when the user did not grant all permissions
        .withFailureHandler(new Runnable() {
            @Override
            public void run() {
                // This is called if the user has rejected one or all of the requested permissions
                L.e(this.getClass().getSimpleName(), "Unable to request permission");

            }
        })

        // After calling this, the user is prompted to grant the rights
        .request();

Погляньте: https://gist.github.com/crysxd/385b57d74045a8bd67c4110c34ab74aa


2
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
    switch (requestCode) {
        case PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
            if (grantResults.length > 0) {
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                    // Denied
                } else {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                        // To what you want
                    } else {
                       // Bob never checked click
                    }
                }
            }
        }
    }
}

2

Спробуйте цю просту бібліотеку дозволів. Він буде обробляти всі операції, пов'язані з дозволом, у 3 простих кроки. Це врятувало мій час. Ви можете закінчити всі роботи, пов'язані з дозволом, за 15 хвилин .

Він може обробляти заборонено дозволи та ін.

https://github.com/ParkSangGwon/TedPermission

Крок 1: додайте свою залежність

dependencies {
     compile 'gun0912.ted:tedpermission:2.1.1'
     //check the above link for latest libraries
}

Крок 2: Запитайте дозволи

TedPermission.with(this)
    .setPermissionListener(permissionlistener)
    .setDeniedMessage("If you reject permission,you can not use this service\n\nPlease turn on permissions at [Setting] > [Permission]")
    .setPermissions(Manifest.permission.READ_CONTACTS, Manifest.permission.ACCESS_FINE_LOCATION)
    .check();

Крок 3: Обробіть відповідь на дозвіл

PermissionListener permissionlistener = new PermissionListener() {
    @Override
    public void onPermissionGranted() {
        Toast.makeText(MainActivity.this, "Permission Granted", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onPermissionDenied(ArrayList<String> deniedPermissions) {
        Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show();
    }
};

Чудово. Це врятувало мій час
Vigneswaran A

Приємний, простий у використанні
Uray Febri

2

ви можете слухати досить.

Слухач

interface PermissionListener {
    fun onNeedPermission()
    fun onPermissionPreviouslyDenied(numberDenyPermission: Int)
    fun onPermissionDisabledPermanently(numberDenyPermission: Int)
    fun onPermissionGranted()
}

MainClass для дозволу

class PermissionUtil {

    private val PREFS_FILENAME = "permission"
    private val TAG = "PermissionUtil"

    private fun shouldAskPermission(context: Context, permission: String): Boolean {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            val permissionResult = ActivityCompat.checkSelfPermission(context, permission)
            if (permissionResult != PackageManager.PERMISSION_GRANTED) {
                return true
            }
        }
        return false
    }

    fun checkPermission(context: Context, permission: String, listener: PermissionListener) {

        Log.i(TAG, "CheckPermission for $permission")

        if (shouldAskPermission(context, permission)) {

            // Load history permission
            val sharedPreference = context.getSharedPreferences(PREFS_FILENAME, 0)
            val numberShowPermissionDialog = sharedPreference.getInt(permission, 0)

            if (numberShowPermissionDialog == 0) {

                (context as? Activity)?.let {
                    if (ActivityCompat.shouldShowRequestPermissionRationale(it, permission)) {
                        Log.e(TAG, "User has denied permission but not permanently")
                        listener.onPermissionPreviouslyDenied(numberShowPermissionDialog)
                    } else {
                        Log.e(TAG, "Permission denied permanently.")
                        listener.onPermissionDisabledPermanently(numberShowPermissionDialog)
                    }
                } ?: kotlin.run {
                    listener.onNeedPermission()
                }

            } else {
                // Is FirstTime
                listener.onNeedPermission()
            }


            // Save history permission
            sharedPreference.edit().putInt(permission, numberShowPermissionDialog + 1).apply()


        } else {
            listener.onPermissionGranted()
        }

    }
}

Використовується таким способом

      PermissionUtil().checkPermission(this, Manifest.permission.ACCESS_FINE_LOCATION,
                object : PermissionListener {
                    override fun onNeedPermission() {
                        log("---------------------->onNeedPermission")

//                            ActivityCompat.requestPermissions(this@SplashActivity,
//                                    Array(1) { Manifest.permission.ACCESS_FINE_LOCATION },
//                                    118)

                    }

                    override fun onPermissionPreviouslyDenied(numberDenyPermission: Int) {
                        log("---------------------->onPermissionPreviouslyDenied")
                    }

                    override fun onPermissionDisabledPermanently(numberDenyPermission: Int) {
                        log("---------------------->onPermissionDisabled")
                    }

                    override fun onPermissionGranted() {
                        log("---------------------->onPermissionGranted")
                    }

                })

переосмислювати onRequestPermissionsResult у діяльності або fragmnet

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
 if (requestCode == 118) {
        if (permissions[0] == Manifest.permission.ACCESS_FINE_LOCATION && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getLastLocationInMap()
        }
        }
    }

1

Натомість ви отримаєте зворотний дзвінок у onRequestPermissionsResult()форматі PERMISSION_DENIED, коли ви знову надішлете запит на отримання дозволу, потрапляючи в помилковий станshouldShowRequestPermissionRationale()

Від документа до Android:

Коли система просить користувача надати дозвіл, користувач має можливість сказати системі більше не просити цього дозволу. У цьому випадку, кожного разу, коли додаток requestPermissions()знову запитує цей дозвіл, система негайно відхиляє запит. Система викликає ваш onRequestPermissionsResult()метод зворотного виклику і проходить PERMISSION_DENIEDтак само, як і коли б користувач явно відхилив ваш запит знову. Це означає, що під час дзвінка requestPermissions()ви не можете припустити, що якась пряма взаємодія з користувачем відбулася.


1

Ви можете використовувати if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)метод, щоб визначити, перевіряється чи ні запитувати ніколи.

Для отримання додаткової інформації: Перевірте це

Щоб перевірити наявність кількох дозволів, використовуйте:

  if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)
                                || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.RECORD_AUDIO)) {
                            showDialogOK("Service Permissions are required for this app",
                                    new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialog, int which) {
                                            switch (which) {
                                                case DialogInterface.BUTTON_POSITIVE:
                                                    checkAndRequestPermissions();
                                                    break;
                                                case DialogInterface.BUTTON_NEGATIVE:
                                                    // proceed with logic by disabling the related features or quit the app.
                                                    finish();
                                                    break;
                                            }
                                        }
                                    });
                        }
                        //permission is denied (and never ask again is  checked)
                        //shouldShowRequestPermissionRationale will return false
                        else {
                            explain("You need to give some mandatory permissions to continue. Do you want to go to app settings?");
                            //                            //proceed with logic by disabling the related features or quit the app.
                        }

роз'яснити () метод

private void explain(String msg){
        final android.support.v7.app.AlertDialog.Builder dialog = new android.support.v7.app.AlertDialog.Builder(this);
        dialog.setMessage(msg)
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        //  permissionsclass.requestPermission(type,code);
                        startActivity(new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:com.exampledemo.parsaniahardik.marshmallowpermission")));
                    }
                })
                .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface paramDialogInterface, int paramInt) {
                        finish();
                    }
                });
        dialog.show();
    }

Наведений вище код також покаже діалогове вікно, яке перенаправить користувача на екран налаштувань програми, звідки він може дати дозвіл, якщо не поставив галочку ніколи не запитувати знову.


1

Можна використовувати

shouldShowRequestPermissionRationale()

всередині

onRequestPermissionsResult()

Дивіться приклад нижче:

Перевірте, чи має він дозвіл, коли користувач натискає кнопку:

@Override
public void onClick(View v) {
    if (v.getId() == R.id.appCompatBtn_changeProfileCoverPhoto) {
        if (Build.VERSION.SDK_INT < 23) { // API < 23 don't need to ask permission
            navigateTo(MainActivity.class); // Navigate to activity to change photos
        } else {
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    != PackageManager.PERMISSION_GRANTED) {
                // Permission is not granted yet. Ask for permission...
                requestWriteExternalPermission();
            } else {
                // Permission is already granted, good to go :)
                navigateTo(MainActivity.class);
            }
        } 
    }
}

Коли користувач відповість у діалоговому вікні дозволу, ми перейдемо до OnRequestPermissionResult:

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);

    if (requestCode == WRITE_EXTERNAL_PERMISSION_REQUEST_CODE) {
        // Case 1. Permission is granted.  
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {  
            if (ContextCompat.checkSelfPermission(SettingsActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                // Before navigating, I still check one more time the permission for good practice.
                navigateTo(MainActivity.class);
            }
        } else { // Case 2. Permission was refused
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                // Case 2.1. shouldShowRequest... returns true because the
                // permission was denied before. If it is the first time the app is running we will 
                // end up in this part of the code. Because he need to deny at least once to get 
                // to onRequestPermissionsResult. 
                Snackbar snackbar = Snackbar.make(findViewById(R.id.relLayout_container), R.string.you_must_verify_permissions_to_send_media, Snackbar.LENGTH_LONG);
                snackbar.setAction("VERIFY", new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        ActivityCompat.requestPermissions(SettingsActivity.this
                                , new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}
                                , WRITE_EXTERNAL_PERMISSION_REQUEST_CODE);
                    }
                });
                snackbar.show();
            } else {
                // Case 2.2. Permission was already denied and the user checked "Never ask again". 
                // Navigate user to settings if he choose to allow this time.
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                builder.setMessage(R.string.instructions_to_turn_on_storage_permission)
                        .setPositiveButton(getString(R.string.settings), new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent settingsIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                                Uri uri = Uri.fromParts("package", getPackageName(), null);
                                settingsIntent.setData(uri);
                                startActivityForResult(settingsIntent, 7);
                            }
                        })
                        .setNegativeButton(getString(R.string.not_now), null);
                Dialog dialog = builder.create();
                dialog.show();
            }
        }
    }

}

0

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

Я хотів би запропонувати спочатку функцію, що стосується дозволу. Якщо користувач користується ним і не має прав, він отримує або перше діалогове вікно, або 2-е і 3-е. Коли користувач вибрав "Ніколи більше не запитувати", я б хотів відключити функціональність і відобразити її по-іншому. - Моя дія ініціюється введенням тексту текстури, я також хотів би додати текст (дозволу скасовано) 'до відображеного тексту мітки. Це показує користувачеві: "Є функціонал, але я не можу ним користуватися через мої налаштування дозволу." Однак це здається неможливим, тому що я не можу перевірити, було обрано «Ніколи більше не запитувати».

Я прийшов до рішення, з яким я можу жити, завдяки тому, щоб моя функціональність завжди була включена за допомогою активної перевірки дозволу. Я показую повідомлення Toast у onRequestPermissionsResult () у разі негативної відповіді, але лише у тому випадку, якщо я не показав спливаюче вікно власного обґрунтування. Тож якщо користувач обрав "Ніколи більше не запитуй", він отримує лише тост-повідомлення. Якщо користувач не бажає вибирати "ніколи більше не запитувати", він отримує лише спеціальне обгрунтування та запит на дозвіл, що з'являється операційною системою, але не тост, оскільки три повідомлення поспіль будуть надто сильними болями.


0

Я маю реалізувати динамічний дозвіл на камеру. Де трапляються 3 можливі випадки: 1. Дозволити, 2. Заборонити, 3. Не запитувати знову.

 @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

    for (String permission : permissions) {
        if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {
            //denied
            Log.e("denied", permission);
        } else {
            if (ActivityCompat.checkSelfPermission(getActivity(), permission) == PackageManager.PERMISSION_GRANTED) {
                //allowed
                Log.e("allowed", permission);
            } else {
                //set to never ask again
                Log.e("set to never ask again", permission);
                //do something here.
            }
        }
    }
    if (requestCode != MaterialBarcodeScanner.RC_HANDLE_CAMERA_PERM) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        return;
    }
    if (grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
        return;
    } else {
        //set to never ask again
        Log.e("set to never ask again", permissions[0]);
    }
    DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int id) {
            dialog.cancel();
        }
    };
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setTitle("Error")
            .setMessage(R.string.no_camera_permission)
            .setPositiveButton(android.R.string.ok, listener)
            .show();


}

private void insertDummyContactWrapper() {
        int hasWriteContactsPermission = checkSelfPermission(Manifest.permission.CAMERA);
        if (hasWriteContactsPermission != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.CAMERA},
                    REQUEST_CODE_ASK_PERMISSIONS);
            return;
        }
        mScannerView.setResultHandler(this);
        mScannerView.startCamera(mCameraId);
        mScannerView.setFlash(mFlash);
        mScannerView.setAutoFocus(mAutoFocus);
    }

private int checkSelfPermission(String camera) {
    if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.CAMERA)
            != PackageManager.PERMISSION_GRANTED) {
        return REQUEST_CODE_ASK_PERMISSIONS;
    } else {
        return REQUEST_NOT_CODE_ASK_PERMISSIONS;
    }
}

0

Розширюючись на відповідь mVck вище, наступна логіка визначає, чи було перевірено "Ніколи більше не запитувати" для заданого запиту дозволу:

bool bStorage = grantResults[0] == Permission.Granted;
bool bNeverAskForStorage =
    !bStorage && (
        _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
        _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
    );

який витягнутий знизу (для повного прикладу див. цю відповідь )

private bool _bStorageRationaleBefore;
private bool _bStorageRationaleAfter;        
private const int ANDROID_PERMISSION_REQUEST_CODE__SDCARD = 2;
//private const int ANDROID_PERMISSION_REQUEST_CODE__CAMERA = 1;
private const int ANDROID_PERMISSION_REQUEST_CODE__NONE = 0;

public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
{
    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);

    switch (requestCode)
    {
        case ANDROID_PERMISSION_REQUEST_CODE__SDCARD:               
            _bStorageRationaleAfter = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
            bool bStorage = grantResults[0] == Permission.Granted;
            bool bNeverAskForStorage =
                !bStorage && (
                    _bStorageRationaleBefore == true  && _bStorageRationaleAfter == false ||
                    _bStorageRationaleBefore == false && _bStorageRationaleAfter == false
                );      
            break;                
    }
}

private List<string> GetRequiredPermissions(out int requestCode)
{
    // Android v6 requires explicit permission granting from user at runtime for security reasons            
    requestCode = ANDROID_PERMISSION_REQUEST_CODE__NONE; // 0
    List<string> requiredPermissions = new List<string>();

    _bStorageRationaleBefore = ShouldShowRequestPermissionRationale(Android.Manifest.Permission.WriteExternalStorage);
    Permission writeExternalStoragePerm = ApplicationContext.CheckSelfPermission(Android.Manifest.Permission.WriteExternalStorage);
    //if(extStoragePerm == Permission.Denied)
    if (writeExternalStoragePerm != Permission.Granted)
    {
        requestCode |= ANDROID_PERMISSION_REQUEST_CODE__SDCARD;
        requiredPermissions.Add(Android.Manifest.Permission.WriteExternalStorage);
    }

    return requiredPermissions;
}

protected override void OnCreate(Bundle savedInstanceState)
{
    base.OnCreate(savedInstanceState);

        // Android v6 requires explicit permission granting from user at runtime for security reasons
        int requestCode;
        List<string> requiredPermissions = GetRequiredPermissions(out requestCode);
        if (requiredPermissions != null && requiredPermissions.Count > 0)
        {
            if (requestCode >= ANDROID_PERMISSION_REQUEST_CODE__SDCARD)                    
            {
                _savedInstanceState = savedInstanceState;
                RequestPermissions(requiredPermissions.ToArray(), requestCode);
                return;
            }
        }
    }            

    OnCreate2(savedInstanceState);
}

0

ви можете прочитати офіційний документ Android Запит дозволів на додаток

або ви можете знайти багато популярних бібліотек дозволів для Android на Github


2
Просто запропонувати бібліотеку - це не надто приємність: принаймні поясніть, як їх використовувати для вирішення проблеми.
М. Прохоров

0

Щоб точно відповісти на питання, що відбувається, коли користувач натисне "Ніколи не запитуй знову"?

Перевищений метод / функція

onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray)

Масив grantResult виявляється порожнім, тож ви можете щось там зробити? Але не найкраща практика.

Як впоратися з "Ніколи не питати знову"?

Я працюю з Fragment, на який потрібен READ_EXTERNAL_STORAGE дозвіл.

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        when {
            isReadPermissionsGranted() -> {

                /**
                 * Permissions has been Granted
                 */

                getDirectories()
            }

            isPermissionDeniedBefore() -> {

                /**
                 * User has denied before, explain why we need the permission and ask again
                 */

                updateUIForDeniedPermissions()
                checkIfPermissionIsGrantedNow()

            }
            else -> {

                /**
                 * Need to ask For Permissions, First Time
                 */

                checkIfPermissionIsGrantedNow()

                /**
                 * If user selects, "Dont Ask Again" it will never ask again! so just update the UI for Denied Permissions
                 */

                updateUIForDeniedPermissions()

            }
        }
    }

Інші функції тривіальні.

// Is Read Write Permissions Granted
fun isReadWritePermissionGranted(context: Context): Boolean {
    return (ContextCompat.checkSelfPermission(
        context as Activity,
        Manifest.permission.READ_EXTERNAL_STORAGE
    ) == PackageManager.PERMISSION_GRANTED) and
            (ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) == PackageManager.PERMISSION_GRANTED)
}

fun isReadPermissionDenied(context: Context) : Boolean {
    return ActivityCompat.shouldShowRequestPermissionRationale(
        context as Activity,
        PermissionsUtils.READ_EXTERNAL_STORAGE_PERMISSIONS)
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.