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

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

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

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

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

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

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

Предварительный просмотр разработчика 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); 

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

Вы можете проверить 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 делают именно это для разрешения местоположения.

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

  @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 теперь также есть очень похожий class 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; 

Может быть полезным для кого-то: –

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

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

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

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

Вы можете определить это, проверив, следует ли onRequestPermissionsResult() внутри 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). } } } 

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

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

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

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

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

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

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

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

Он может обрабатывать Deny, он может обрабатывать Никогда не спрашивать снова, Он может вызывать настройки приложения для разрешения, Он может дать сообщение 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 deniedPermissions) { Toast.makeText(MainActivity.this, "Permission Denied\n" + deniedPermissions.toString(), Toast.LENGTH_SHORT).show(); } }; 

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

 /** * 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. } } } 

Я написал сокращенную версию запроса на разрешение в 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 Le(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

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

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

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

  mAskedPermissionTime = System.currentTimeMillis(); 

Затем в onRequestPermissionsResult

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

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

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

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

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

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

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

 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 } 

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

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

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

I would also like to obtain the information whether or not the user has selected “never ask again”. I have achieved a ‘almost solution’ with an ugly looking flag, but before I tell you how, I will tell you about my motivation:

I would like to offer the permission referring functionality initially. If the user uses it and has no rights, he/she gets the either the 1th dialog from above or both the 2nd and 3rd. When the user has chosen ‘Never ask again’ I would like to disable the functionality and to display it differently. – My action is triggered by a spinner text entry, I would also like to add ‘(Permission revoked)’ to the label text displayed. This shows to the user: ‘There is functionality but I cannot use it, due to my permission settings.’ However, this does not seem to be possible, as I cannot check whether or not ‘Never ask again’ has been chosen.

I came to a solution I can live with by having my functionality always enabled with an active permission check. I am showing a Toast message in onRequestPermissionsResult() in case of a negative response but only if I have not shown my custom rationale popup. So if the user has chosen ‘Never ask again’ he gets a toast message only. If the user is reluctant to chose ‘never ask again’ he gets only the custom rationale and the permission request popup by the operation system but not toast, as three notifications in a row would be too much pain.

Instead you will receive callback on onRequestPermissionsResult() as PERMISSION_DENIED when you request permission again while falling in false condition of shouldShowRequestPermissionRationale()

From Android doc:

When the system asks the user to grant a permission, the user has the option of telling the system not to ask for that permission again. In that case, any time an app uses requestPermissions() to ask for that permission again, the system immediately denies the request. The system calls your onRequestPermissionsResult() callback method and passes PERMISSION_DENIED , the same way it would if the user had explicitly rejected your request again. This means that when you call requestPermissions() , you cannot assume that any direct interaction with the user has taken place.

I have to implement dynamic permission for camera. Where 3 possible cases occurs: 1. Allow, 2. Denied, 3. Don’t ask again.

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

You can use if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) method to detect whether never ask is checked or not.

For more reference : Check this

To check for multiple permissions use:

  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() method

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

Above code will also show dialog, which will redirect user to app settings screen from where he can give permission if had checked never ask again button.

Expanding on mVck ‘s answer above, the following logic determines whether “Never ask again” has been checked for a given Permission Request:

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

which is excerpted from below (for the full example see this answer )

 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 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 requiredPermissions = new List(); _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 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); } 
 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 } } } } } } 
Давайте будем гением компьютера.