Каким образом мы отличаем никогда не просил у стоп-спрашивать разрешения во время выполнения Андроид м?



Когда дело доходит до разрешений среды выполнения M Developer Preview, согласно Google :





  1. Если вы никогда раньше не просили определенного разрешения, просто попросите его


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



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



Однако у нас есть только один метод, shouldShowRequestPermissionRationale(), возвращающий a boolean, и у нас есть три состояния. Нам нужен способ отличить состояние "никогда не спрашивай" от состояния "перестань спрашивать", поскольку мы получаем false от shouldShowRequestPermissionRationale() для обоих.

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



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



В этих случаях мы должны отслеживать, просили ли мы разрешения сами или нет? Или есть что-то в API Android M, что я пропускаю, что говорит нам, спрашивали ли мы раньше или нет?

608   9  

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

Дело-1

Кейс- 2

Дело - 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 одинаковы для разработчика приложений: вам нужно разрешение и ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED, затем вы просто спрашиваете разрешение через ActivityCompat.requestPermissions(), когда пользователь нажимает на функцию, которая требует разрешения, независимо от того, сколько раз вы просили. Пользователь в конечном счете" предоставит "его, или" откажет "его с проверкой" никогда не спрашивайте снова". Конструкция не делает не рекомендуется открывать диалоговое окно запроса разрешений несколько раз.

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

Еще пара пояснений относительно состояния 3:

  1. Да, мы должны перестать беспокоить пользователя, прекратив показывать объяснение, а не просьба остановить. Вот почему они предоставили shouldShowRequestPermissionRationale().
  2. он не утруждает себя сохранением запроса на разрешение. После того, как пользователь выбрал "никогда не спрашивайте снова", ActivityCompat.requestPermissions() больше не будет всплывающего диалогового окна.
  3. лучше отключать соответствующий пользовательский интерфейс каждый раз, когда мы обнаруживаем, что у нас нет разрешения, во время сеанса одного пользователя. Вместо отключения пользовательского интерфейса после 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), и все работает в соответствии с ожиданиями.

  1. если первый раз спрашивали разрешения. В этом случае shouldShowRequestPermissionRationale возвращает false и isLocationPermissionDialogShown также false
  2. во второй раз shouldShowRequestPermissionRationale возвращаем true и, показывая диалог, устанавливаем isLocationPermissionDialogShown на true. и когда мы проверим условие оба будут true
  3. каждый раз, пока никогда не спросите снова тикал shouldShowRequestPermissionRationale return true и isLocationPermissionDialogShown returns true
  4. если никогда больше не спрашивать галочкой 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

    Ничего не найдено.