Android M - проверить разрешение во время выполнения - как определить, проверен ли пользователь "Никогда не спрашивать снова"?

В соответствии с этим: http://developer.android.com/preview/features/runtime-permissions.html#coding приложение может проверять разрешения на время выполнения и запрашивать разрешения, если оно еще не было предоставлено. Затем появится следующий диалог:

enter image description here

В случае, если пользователь отклоняет важное разрешение, в приложении должно отображаться объяснение, почему требуется разрешение и какое влияние имеет снижение. Этот диалог имеет две опции:

  1. повторите попытку (разрешение запрашивается снова)
  2. отрицать (приложение будет работать без этого разрешения).

Однако если пользователь выберет " Never ask again, второе диалоговое окно с объяснением не должно отображаться, особенно если пользователь уже один раз отказался. Теперь вопрос: как мое приложение узнает, проверял ли пользователь " Never ask again? IMO onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) не дает мне эту информацию.

Второй вопрос: есть ли у Google планы по добавлению в диалоговое окно разрешений специального сообщения, объясняющего, почему приложению нужны разрешения? Таким образом, никогда не будет второго диалога, который, несомненно, улучшит работу.

Ответ 1

Предварительный просмотр разработчика 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). Эта метод возвращает true, если приложение запросило это разрешение ранее, и пользователь отклонил запрос. Это означает, что вы должен, вероятно, объяснить пользователю, почему вам нужно разрешение.

Если пользователь отклонил запрос на разрешение в прошлом и выбрал параметр "Не спрашивать снова" в диалоговом окне системы запроса разрешения, этот метод возвращает false. Метод также возвращает 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);

Невозможно отправить пользователя непосредственно на страницу авторизации.

Ответ 2

Вы можете проверить shouldShowRequestPermissionRationale() на onRequestPermissionsResult().

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

Проверьте, было ли разрешение предоставлено или нет в onRequestPermissionsResult(). Если не, проверьте shouldShowRequestPermissionRationale().

  • Если этот метод возвращает true, то объясните, почему это конкретное разрешение необходимо. Затем в зависимости от выбора пользователя снова requestPermissions().
  • Если он возвращает 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 делают именно это для разрешения местоположения.

Ответ 3

Вот хороший и простой способ проверить текущий статус разрешения:

    @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;
    }

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

Update:

В 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;

Ответ 4

Вы можете определить это, проверив, должно ли обоснование разрешения показываться в onRequestPermissionsResult() обратного вызова 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).
        }
    }
}

Ответ 5

Может быть полезно кому-то: -

Что я заметил, если мы проверяем флаг toShowRequestPermissionRationale() на метод обратного вызова onRequestPermissionsResult(), он показывает только два состояния.

Состояние 1: -Return true: - Каждый раз, когда пользователь нажимает "Запретить разрешения" (включая самый первый раз).

Состояние 2: - Возвращает false: - если пользователь выбирает "никогда не спрашивает".

Ссылка подробного рабочего примера

Ответ 6

После того, как пользователь отметил "Не спрашивать снова", вопрос не может быть отображен снова. Но пользователю может быть объяснено, что он ранее отказал в разрешении и должен предоставить разрешение в настройках. И сослаться на него в настройках, используя следующий код:

@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;
}

Ответ 7

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

Создать 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

Ответ 8

У меня была такая же проблема, и я понял это. Чтобы сделать жизнь намного проще, я написал класс 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.

Счастливое кодирование:)

Ответ 9

Полное объяснение для каждого случая разрешения

/**
 *    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 page from here now.
        }

    }
}

Ответ 10

Я написал сокращенную версию запроса на разрешение в Android M. Этот код также поддерживает обратную совместимость с более старыми версиями Android.

Весь уродливый код извлекается во Фрагмент, который прикрепляет и отделяет себя от Activity, запрашивающего разрешения. Вы можете использовать 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

Ответ 11

Метод shouldShowRequestPermissionRationale() может быть пользователем, чтобы проверить, выбрал ли пользователь параметр "никогда не спрашивал снова" и отказал в разрешении. Там много примеров кода, поэтому я бы скорее объяснил, как использовать его для такой цели, потому что я думаю, что его имя и его реализация делают это более сложным, чем оно есть на самом деле.

Как объяснено в Запрос разрешений во время выполнения, этот метод возвращает true, если опция "никогда не спрашивать снова" видна, иначе false; поэтому он возвращает false в первый раз, когда отображается диалог, а затем со второго раза возвращает true, и только если пользователь отклоняет разрешение, выбирающее параметр, в этот момент он снова возвращает false.

Чтобы обнаружить такой случай, вы можете обнаружить последовательность false-true-false или (более простой), вы можете иметь флаг, который отслеживает начальное время, когда отображается диалог. После этого этот метод возвращает либо true, либо false, где false позволяет вам определять, когда выбран этот параметр.

Ответ 12

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

Он может обрабатывать Запретить, может обрабатывать Никогда не спрашивать снова, Может вызывать настройки приложения для разрешения, Может выдавать сообщение Rational, Может выдавать сообщение Отказ, Может давать список принятых разрешений, Может давать список отклоненных. разрешения и т.д.

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();
    }
};

Ответ 13

Полезная функция, чтобы определить, было ли заблокировано произвольное разрешение для запроса (в Kotlin):

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 для первого запроса на разрешение), чтобы различать состояния "Никогда не спрашивал" и "Никогда не спрашивать", как в предыдущей строке: не вернуть эту информацию.

Ответ 14

Пожалуйста, не бросайте мне камни для этого решения.

Это работает, но немного "взломан".

Когда вы вызываете requestPermissions, зарегистрируйте текущее время.

        mAskedPermissionTime = System.currentTimeMillis();

Тогда в onRequestPermissionsResult

Если результат не предоставлен, снова проверьте время.

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

Так как пользователь не мог так быстро нажать на кнопку deny, мы знаем, что он выбрал "никогда не спрашивать снова", потому что обратный вызов мгновен.

Используйте свои риски.

Ответ 15

Ты можешь использовать

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();
            }
        }
    }

}

Ответ 16

Вы можете слушать слушателя довольно.

слушатель

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([email protected],
//                                    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()
        }
        }
    }

Ответ 17

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

Я хотел бы сначала предложить функциональную возможность разрешения. Если пользователь использует его и не имеет прав, он получает либо 1-й диалог сверху, либо 2-й и 3-й. Когда пользователь выбрал "Никогда не спрашивать снова" , я хотел бы отключить функциональность и отобразить ее по-разному. - Мое действие инициируется текстовой записью с прядильщиками, я также хотел бы добавить '(Permission revoked)' в отображаемый текст метки. Это показывает пользователю: "Есть функциональность, но я не могу ее использовать из-за моих настроек разрешения". Однако это не представляется возможным, поскольку я не могу проверить, выбрано ли "Никогда не спрашивать снова" .

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

Ответ 18

Вместо этого вы получите ответ на onRequestPermissionsResult() как PERMISSION_DENIED, когда вы снова запросите разрешение при падении в ложное состояние shouldShowRequestPermissionRationale()

От Android doc:

Когда система просит пользователя предоставить разрешение, пользователь имеет возможность сообщить системе не запрашивать это разрешение снова. В этом случае, когда приложение снова использует requestPermissions(), чтобы запросить это разрешение, система немедленно отклоняет запрос. Система вызывает ваш метод обратного вызова onRequestPermissionsResult() и передает PERMISSION_DENIED, так же, как если бы пользователь явно отклонил ваш запрос еще раз. Это означает, что когда вы вызываете requestPermissions(), вы не можете предположить, что имело место какое-либо прямое взаимодействие с пользователем.

Ответ 19

Вы можете использовать метод 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.
                        }

метод explain()

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();
    }

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

Ответ 20

Мне нужно реализовать динамическое разрешение для камеры. Где возможны 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;
    }
}

Ответ 21

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
                    }
                }
            }
        }
    }
}

Ответ 22

Развернув на 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);
}

Ответ 24

Чтобы точно ответить на вопрос, что происходит, когда пользователь нажимает "Никогда не спрашивать"?

Переопределенный метод/функция

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)
}