Каким образом мы отличаем никогда не просил у стоп-спрашивать разрешения во время выполнения Андроид м?
Когда дело доходит до разрешений среды выполнения M Developer Preview, согласно Google :
Если вы никогда раньше не просили определенного разрешения, просто попросите его
Если вы спросили раньше, и пользователь сказал "нет", а затем пользователь пытается сделать что-то, что требует отклоненного разрешения, вы должны предложить пользователю объяснить, почему вам нужно разрешение, прежде чем вы снова запросите разрешение
Если бы вы спросили ... пару раз до этого, и пользователь сказал "нет, и перестаньте спрашивать" (через флажок в диалоговом окне разрешения среды выполнения), вы должны просто перестать беспокоить (например, отключить пользовательский интерфейс, который требует разрешения)
Однако у нас есть только один метод,
shouldShowRequestPermissionRationale(), возвращающий a boolean, и у нас есть три состояния. Нам нужен способ отличить состояние "никогда не спрашивай" от состояния "перестань спрашивать", поскольку мы получаем false от shouldShowRequestPermissionRationale() для обоих.Для разрешений, запрашиваемых при первом запуске приложения, это не большая проблема. Существует множество рецептов для определения того, что это, вероятно, первый запуск вашего приложения (например, значение boolean в SharedPreferences), и поэтому вы предполагаете, что если это первый запуск вашего приложения, вы находитесь в состоянии никогда не спрашиваемого.
Однако часть концепции разрешений среды выполнения заключается в том, что вы можете не запрашивать их все заранее. Разрешения, привязанные к внешним функциям, вы можете запросить только позже, когда пользователь нажимает на что-то, что требует этого разрешения. Вот, пожалуйста. приложение может быть запущено много раз, в течение нескольких месяцев, прежде чем нам вдруг понадобится запросить другое разрешение.
В этих случаях мы должны отслеживать, просили ли мы разрешения сами или нет? Или есть что-то в API Android M, что я пропускаю, что говорит нам, спрашивали ли мы раньше или нет?
9 ответов:
В соответствии с текущим примером: https://github.com/googlesamples/android-RuntimePermissions/blob/master/Application/src/main/java/com/example/android/system/runtimepermissions/MainActivity.java#L195
@Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_CAMERA) { if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { doThing(); //STORE FALSE IN SHAREDPREFERENCES } else { //STORE TRUE IN SHAREDPREFERENCES } }Сохраните логическое значение в SharedPreferences с ключом в качестве кода разрешения и значения, как указано выше, чтобы указать, было ли это предпочтение отклонено ранее.
К сожалению, вы, вероятно, не можете проверить предпочтение, которое было принято, а затем отвергнуто пока ваше приложение работает. Окончательная спецификация недоступна, но есть вероятность, что ваше приложение либо перезапустится, либо получит фиктивные значения до следующего запуска.
Я знаю, что публикую очень поздно, но подробный пример может быть полезен для кого-то.
Я заметил, что если мы проверяем флаг shouldShowRequestPermissionRationale () в методе обратного вызова onRequestPermissionsResult (), он показывает только два состояния.
Состояние 1: - Return true:-- каждый раз, когда пользователь нажимает кнопку запретить разрешения (включая самый первый раз.
Состояние 2: - возвращает false: - если пользователь выбирает s " никогда не спрашивает снова.
Вот пример с несколькими запрос разрешения: -
Приложение нуждается в 2 разрешениях при запуске . SEND_SMS и ACCESS_FINE_LOCATION (оба упомянуты в манифесте.XML).
Как только приложение запускается, оно запрашивает несколько разрешений одновременно. Если оба разрешения предоставлены, нормальный поток идет.
public static final int REQUEST_ID_MULTIPLE_PERMISSIONS = 1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if(checkAndRequestPermissions()) { // carry on the normal flow, as the case of permissions granted. } } private boolean checkAndRequestPermissions() { int permissionSendMessage = ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS); int locationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION); List<String> listPermissionsNeeded = new ArrayList<>(); if (locationPermission != PackageManager.PERMISSION_GRANTED) { listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION); } if (permissionSendMessage != PackageManager.PERMISSION_GRANTED) { listPermissionsNeeded.add(Manifest.permission.SEND_SMS); } if (!listPermissionsNeeded.isEmpty()) { ActivityCompat.requestPermissions(this, listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),REQUEST_ID_MULTIPLE_PERMISSIONS); return false; } return true; }В случае, если одно или несколько разрешений не предоставлены, activityCompat.requestPermissions () запросит разрешения, и управление перейдет к onRequestPermissionsResult() метод обратного вызова.
Вы должны проверить значение флага shouldShowRequestPermissionRationale () в методе обратного вызова onRequestPermissionsResult ().
Есть только два случая:--
Случай 1: - каждый раз, когда пользователь нажимает кнопку запретить разрешения (включая самый первый раз), он возвращает true. Поэтому, когда пользователь отрицает, мы можем показать больше объяснений и продолжать спрашивать снова.
Случай 2: - только если пользователь выберет "никогда не спрашивает снова", он вернет false. В этом в этом случае мы можем продолжить с ограниченной функциональностью и направлять пользователя, чтобы активировать разрешения из настроек для более функциональных возможностей, или мы можем закончить установку, если разрешения тривиальны для приложения.
Кейс- 1
Кейс- 2
@Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { Log.d(TAG, "Permission callback called-------"); switch (requestCode) { case REQUEST_ID_MULTIPLE_PERMISSIONS: { Map<String, Integer> perms = new HashMap<>(); // Initialize the map with both permissions perms.put(Manifest.permission.SEND_SMS, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED); // Fill with actual results from user if (grantResults.length > 0) { for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); // Check for both permissions if (perms.get(Manifest.permission.SEND_SMS) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { Log.d(TAG, "sms & location services permission granted"); // process the normal flow //else any one or both the permissions are not granted } else { Log.d(TAG, "Some permissions are not granted ask again "); //permission is denied (this is the first time, when "never ask again" is not checked) so ask again explaining the usage of permission // // shouldShowRequestPermissionRationale will return true //show the dialog or snackbar saying its necessary and try again otherwise proceed with setup. if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.SEND_SMS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { showDialogOK("SMS and Location Services Permission 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. break; } } }); } //permission is denied (and never ask again is checked) //shouldShowRequestPermissionRationale will return false else { Toast.makeText(this, "Go to settings and enable permissions", Toast.LENGTH_LONG) .show(); // //proceed with logic by disabling the related features or quit the app. } } } } } } private void showDialogOK(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(this) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", okListener) .create() .show(); }
Нет, вам не нужно отслеживать, просили ли вы разрешения или нет, и вам не нужно отличать "никогда не спрашивай" от "перестань спрашивать".
Состояния 1 и 3 одинаковы для разработчика приложений: вам нужно разрешение и
Тем не менее, дизайн действительно побуждает вас объяснить цель разрешения в какой - то момент-ваше состояние 2.ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED, затем вы просто спрашиваете разрешение черезActivityCompat.requestPermissions(), когда пользователь нажимает на функцию, которая требует разрешения, независимо от того, сколько раз вы просили. Пользователь в конечном счете" предоставит "его, или" откажет "его с проверкой" никогда не спрашивайте снова". Конструкция не делает не рекомендуется открывать диалоговое окно запроса разрешений несколько раз.shouldShowRequestPermissionRationale()не используется для определения того, следует ли запрашивать разрешение, он используется для определения того, следует ли показывать объяснения, прежде чем запрашивать разрешение.Еще пара пояснений относительно состояния 3:
- Да, мы должны перестать беспокоить пользователя, прекратив показывать объяснение, а не просьба остановить. Вот почему они предоставили
shouldShowRequestPermissionRationale().- он не утруждает себя сохранением запроса на разрешение. После того, как пользователь выбрал "никогда не спрашивайте снова",
ActivityCompat.requestPermissions()больше не будет всплывающего диалогового окна.- лучше отключать соответствующий пользовательский интерфейс каждый раз, когда мы обнаруживаем, что у нас нет разрешения, во время сеанса одного пользователя. Вместо отключения пользовательского интерфейса после
shouldShowRequestPermissionRationale()верните false.
У меня есть подход к решению вашей проблемы, он, кажется, работает довольно хорошо для меня.
Я отличаю Never-Asked от Stop-Asking, используя SharedPreferences, я приведу вам пример того, как я это использую.private void requestAccountPermission() { SharedPreferences mPreferences = getSharedPreferences("configuration", MODE_PRIVATE); boolean firstTimeAccount = mPreferences.getBoolean("firstTimeAccount", true); if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.GET_ACCOUNTS)) { // 2. Asked before, and the user said "no" ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS}, REQUEST_CODE_ACCOUNTS); }else { if(firstTimeAccount) { // 1. first time, never asked SharedPreferences.Editor editor = mPreferences.edit(); editor.putBoolean("firstTimeAccount", false); editor.commit(); // Account permission has not been granted, request it directly. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.GET_ACCOUNTS},REQUEST_CODE_ACCOUNTS); }else{ // 3. If you asked a couple of times before, and the user has said "no, and stop asking" // Your code } } }
Вот метод для отслеживания, когда диалог разрешения был показан в первый раз, когда пользователь проверил никогда не спрашивать снова и когда разрешение непосредственно отказано после того, как пользователь проверил никогда не спрашивать снова для этого нам нужно сохранить флаг, если диалог обоснования разрешения был показан до получения результата в onRequestPermissionsResult. При необходимости вызовите метод checkPermission ().
public boolean mPermissionRationaleDialogShown = false; public void checkPermission() { if (ContextCompat.checkSelfPermission(this, "PermissionName") != PackageManager.PERMISSION_GRANTED) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName")) { showPermissionRequiredDialog(); } else { askPermission(); } } else { // Permission Granted } } public void askPermission() { ActivityCompat.requestPermissions(this, new String[]{"PermissionName"}, permissionRequestCode); } public void showPermissionRequiredDialog() { mPermissionRationaleDialogShown = true; // Dialog to show why permission is required } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == PERMISSION_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // Permission Granted } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName") && !mPermissionRationaleDialogShown) { // Permission dialog was shown for first time } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, "PermissionName") && mPermissionRationaleDialogShown){ // User deny permission without Never ask again checked } else if (!ActivityCompat.shouldShowRequestPermissionRationale(this, PERMISSION_READ_EXTERNAL) && mPermissionRationaleDialogShown) { // User has checked Never ask again during this permission request } else { // No permission dialog shown to user has user has previously checked Never ask again. Here we can show dialog to open setting screen to change permission } } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
Попробовав все ответы здесь и некоторые другие сообщения через интернет. Я узнал, что мне нужно использовать sharedPreference
isLocationPermissionDialogShown(по умолчанию false), и все работает в соответствии с ожиданиями.
- если первый раз спрашивали разрешения. В этом случае
shouldShowRequestPermissionRationaleвозвращаетfalseиisLocationPermissionDialogShownтакжеfalse- во второй раз
shouldShowRequestPermissionRationaleвозвращаемtrueи, показывая диалог, устанавливаемisLocationPermissionDialogShownнаtrue. и когда мы проверим условие оба будутtrue- каждый раз, пока никогда не спросите снова тикал
shouldShowRequestPermissionRationalereturntrueиisLocationPermissionDialogShownreturnstrue- если никогда больше не спрашивать галочкой
shouldShowRequestPermissionRationaleвозвращаетfalseиisLocationPermissionDialogShownвозвращаетtrue. Именно это нам и нужно.Пожалуйста, проверьте рабочий пример.
Надеюсь, это поможет.public class MainActivity extends AppCompatActivity { SharedPreferences sharedPreferences; String locationPermission; String prefLocationPermissionKey = "isLocationPermissionDialogShown"; private final int PERMISSION_REQUEST_CODE_LOCATION = 1001; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); locationPermission = Manifest.permission.ACCESS_FINE_LOCATION; sharedPreferences = getSharedPreferences("configuration", MODE_PRIVATE); //check for android version if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //Check for permission if (checkSelfPermission(locationPermission) != PackageManager.PERMISSION_GRANTED) { //check if clarification dialog should be shown. if (shouldShowRequestPermissionRationale(locationPermission)) { showClarificationDialog(locationPermission, PERMISSION_REQUEST_CODE_LOCATION); } else { requestPermissions(new String[] { locationPermission}, PERMISSION_REQUEST_CODE_LOCATION); } } else { Log.d("nets-debug", "permission already grranted"); } } } @Override @TargetApi(Build.VERSION_CODES.M) public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { //for location permission if (requestCode == PERMISSION_REQUEST_CODE_LOCATION) { boolean isLocationPermissionDialogShown = sharedPreferences.getBoolean(prefLocationPermissionKey, false); if (!shouldShowRequestPermissionRationale(locationPermission) && isLocationPermissionDialogShown) { // user selected Never Ask Again. do something Log.d("nets-debug", "never ask again"); } else { // all other conditions like first time asked, previously denied etc are captured here and can be extended if required. Log.d("nets-debug", "all other cases"); } } } } @TargetApi(Build.VERSION_CODES.M) public void showClarificationDialog(final String permission, final int requestCode) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Permission Required"); builder.setMessage("Please grant Location permission to use all features of this app"); builder.setPositiveButton("Grant", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(prefLocationPermissionKey, true); editor.apply(); requestPermissions(new String[] {permission}, requestCode); } }); builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(getApplicationContext(), "This permission required", Toast.LENGTH_LONG).show(); } }); builder.create().show(); } }
Вы можете посмотреть здесь - есть блок-схема, которая довольно хорошо объясняет процесс. Он также объясняет, Когда вы должны вызвать
shouldShowRequestPermissionRationale()и когда он возвращает true.В основном в соответствии с документацией Android, вы должны всегда спрашивать разрешения, если у вас его нет (Android автоматически вернет отказ в обратном вызове, если пользователь сказал, что никогда не спрашивал снова), и вы должны отображать короткое сообщение, если пользователь уже отказался от вас один раз в прошлом, но не отметил никогда. спросите еще раз вариант.
ИТАК, НАКОНЕЦ-ТО ПРИШЛО МОЕ ВРЕМЯ ОТВЕТИТЬ НА ВОПРОС ИЗ COMMONSWARE
бизнес-поток:-
1. когда пользователь нажимает на кнопку" запретить разрешение " в первый раз, я покажу диалог обоснования, чтобы объяснить необходимость разрешения. Затем, если пользователь нажимает на кнопку " Отмена "в диалоге обоснования, я покажу тост, показывающий сообщение"Пожалуйста, дайте разрешение на получение местоположения".
2. После этого, когда пользователь нажмите кнопку запретить разрешение (не спрашивайте снова) в диалоговом окне разрешений, я покажу сообщение "Пожалуйста, дайте разрешение на местоположение из настроек приложения". Обратите внимание, что я добавил слова "из настроек приложения", потому что пользователь поставил флажок "не спрашивать снова".
3. так что с этого момента диалог разрешения не будет отображаться.Также не будет показан диалог обоснование.
таким образом, ключ здесь заключается в том, что если и диалог разрешения, и диалог обоснования не показано, то это означает, что пользователь установил флажок "не спрашивать снова".
код:-
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { if(ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.ACCESS_FINE_LOCATION)){ AlertDialogHelper.showDialogWithYesNoCallback(mContext, getString(R.string.confirm), getString(R.string.please_give_permission_to_get_location), new onItemClickReturnBoolean() { @Override public void onItemClick(Boolean status) { if(status){ ActivityCompat.requestPermissions(SplashScreenActivity.this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE); } else{ ShowToast.showShortToast(SplashScreenActivity.this,getString(R.string.please_give_permission_to_get_location)); finish(); } } }); } else{ ActivityCompat.requestPermissions(this,permissions,AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE); } } else{ gettingLocationAfterPermissionGranted(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if(requestCode == AppConfig.FINE_LOCATION_PERMISSION_REQUEST_CODE){ if(grantResults[0] == PackageManager.PERMISSION_GRANTED){ gettingLocationAfterPermissionGranted(); } else{ if(ActivityCompat.shouldShowRequestPermissionRationale(SplashScreenActivity.this,Manifest.permission.ACCESS_FINE_LOCATION)){ ShowToast.showShortToast(this,getString(R.string.please_give_permission_to_get_location)); } else{ ShowToast.showShortToast(this,getString(R.string.please_give_location_permission_from_app_settings)); } finish(); } } }Проверьте этот репозиторий: https://github.com/debChowdhury/PermissionHelperEasy
Easy peasy
Нет необходимости создавать параллельное постоянное состояние для состояния разрешения, вы можете просто использовать этот метод, который возвращает текущее состояние разрешения в любое время:
@Retention(RetentionPolicy.SOURCE) @IntDef({GRANTED, DENIED, BLOCKED}) public @interface PermissionStatus {} public static final int GRANTED = 0; public static final int DENIED = 1; public static final int BLOCKED = 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; } return DENIED; } return GRANTED; }Предостережение: возврат заблокировал первый запуск приложения, прежде чем пользователь принял / отклонил разрешение через приглашение пользователя (на устройствах sdk 23+)



Comments