web-dev-qa-db-fra.com

Comment distinguer les demandes non demandées des demandes d'arrêt dans les autorisations d'exécution d'Android M?

En ce qui concerne les autorisations d'exécution M Developer Preview, selon Google :

  1. Si vous n'avez jamais demandé une permission, demandez-la simplement

  2. Si vous avez déjà demandé, et que l'utilisateur a répondu "non", et que l'utilisateur essaie ensuite de faire quelque chose qui nécessite l'autorisation refusée, vous devez l'inviter à expliquer pourquoi vous avez besoin de l'autorisation, avant de demander à nouveau l'autorisation.

  3. Si vous avez déjà demandé plusieurs fois et que l'utilisateur a répondu «non, et arrêtez de demander» (via la case à cocher de la boîte de dialogue des autorisations d'exécution), arrêtez simplement de vous déranger (par exemple, désactivez l'interface utilisateur qui requiert l'autorisation).

Cependant, nous avons une seule méthode, shouldShowRequestPermissionRationale(), renvoyant une boolean, et nous avons trois états. Nous avons besoin d’un moyen de distinguer l’état jamais demandé de l’état stop, en obtenant false de shouldShowRequestPermissionRationale() pour les deux.

Pour les autorisations demandées lors de la première exécution de l'application, ce n'est pas un gros problème. Il existe de nombreuses recettes pour déterminer qu'il s'agit probablement de la première exécution de votre application (par exemple, boolean valeur dans SharedPreferences), et vous supposez donc que si c'est la première exécution de votre application, vous êtes dans l'état jamais demandé.

Cependant, une partie de la vision des autorisations d'exécution est que vous ne pouvez pas toutes les demander à l'avance. Autorisations liées à des fonctionnalités marginales que vous pourriez ne demander que plus tard, lorsque l'utilisateur appuie sur quelque chose qui nécessite cette autorisation. Ici, l'application peut avoir été exécutée plusieurs fois, pendant des mois, avant que nous ayons soudainement besoin de demander une autre permission.

Dans ces cas, sommes-nous censés déterminer si nous avons demandé la permission nous-mêmes? Ou y a-t-il quelque chose dans l'API Android M qui me manque qui nous indique si nous avons déjà demandé ou non?

61
CommonsWare

Selon l'exemple actuel: 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
        }
    }

Stockez un booléen dans SharedPreferences avec key comme code de permission et comme indiqué ci-dessus, pour indiquer si cette préférence a déjà été refusée.

Malheureusement, vous ne pouvez probablement pas vérifier une préférence qui a été acceptée puis refusée pendant que votre application est en cours d'exécution. La spécification finale n'est pas disponible, mais il est possible que votre application soit redémarrée ou qu'elle obtienne des valeurs fictives jusqu'au prochain lancement.

13
MLProgrammer-CiM

Je sais que je poste très tard, mais des exemples détaillés peuvent être utiles à quelqu'un.

Ce que j'ai remarqué, c'est que, si nous actionnons l'indicateur shouldShowRequestPermissionRationale () dans la méthode de rappel onRequestPermissionsResult (), il n'affiche que deux états.

Etat 1: -Return true: - Chaque fois que l'utilisateur clique sur Refuser les autorisations (y compris la toute première fois).

Etat 2: -Retourne false: - si l'utilisateur sélectionne s “ne demande plus jamais.

Voici un exemple avec plusieurs demandes d'autorisation: -

L'application a besoin de 2 autorisations au démarrage. SEND_SMS et ACCESS_FINE_LOCATION (les deux sont mentionnés dans le fichier manifest.xml).

Dès que l'application démarre, elle demande simultanément plusieurs autorisations. Si les deux autorisations sont accordées, le flux normal est rétabli. 

 enter image description here

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

Si une ou plusieurs autorisations ne sont pas accordées, ActivityCompat.requestPermissions () demandera des autorisations et le contrôle ira à la méthode de rappel onRequestPermissionsResult ().

Vous devez vérifier la valeur de l'indicateur shouldShowRequestPermissionRationale () dans la méthode de rappel onRequestPermissionsResult (). 

Il n'y a que deux cas: -

Cas 1: - À tout moment, l'utilisateur clique sur Refuser les autorisations (y compris la toute première fois), il renvoie la valeur true. Ainsi, lorsque l'utilisateur refuse, nous pouvons afficher plus d'explications et continuer à demander à nouveau.

Cas 2: -Seulement si l'utilisateur sélectionne “ne demande jamais plus”, il retournera faux. Dans ce cas, nous pouvons continuer avec des fonctionnalités limitées et guider l'utilisateur à activer les autorisations à partir des paramètres pour davantage de fonctionnalités, ou nous pouvons terminer la configuration, si les autorisations sont triviales pour l'application.

CAS 1

 Case - 1

CASE- 2

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

Non, vous n'avez pas besoin de savoir si vous avez demandé l'autorisation ou non, et vous n'avez pas besoin de faire la distinction entre Never-Asked et Stop-Asking.

Les états 1 et 3 sont les mêmes pour les développeurs d'applications: vous avez besoin de l'autorisation et de ActivityCompat.checkSelfPermission != PackageManager.PERMISSION_GRANTED. Il vous suffit ensuite de demander l'autorisation via ActivityCompat.requestPermissions(), chaque fois que l'utilisateur sélectionne la fonctionnalité qui nécessite l'autorisation, quel que soit le nombre de demandes. L'utilisateur finira par "l'accorder" ou "le refuser" avec la case "ne plus jamais demander" cochée. La conception ne vous dissuade PAS de faire apparaître plusieurs fois la boîte de dialogue de demande d'autorisation.

Cependant, la conception vous encourage à expliquer le but de l'autorisation à un moment donné - votre état 2. shouldShowRequestPermissionRationale() n'est PAS utilisé pour déterminer si vous devez demander une autorisation, mais pour déterminer si vous devez afficher des explications, AVANT de demander une autorisation .

Quelques explications supplémentaires concernant l’état 3:

  1. Oui, nous devrions cesser de déranger l'utilisateur en cessant d'afficher l'explication, et non en arrêtant la demande. C'est pourquoi ils ont fourni la shouldShowRequestPermissionRationale().
  2. Ce n'est pas la peine de garder la demande d'autorisation. Une fois que l'utilisateur a choisi "ne plus jamais demander", ActivityCompat.requestPermissions() ne fera plus apparaître de boîte de dialogue.
  3. Il est préférable de désactiver l'interface utilisateur appropriée chaque fois que nous découvrons que nous n'avons pas l'autorisation, au cours d'une session avec un seul utilisateur. Au lieu de désactiver l'interface utilisateur après shouldShowRequestPermissionRationale(), renvoyez false.
6
ed9er

J'ai une approche de la solution à votre problème, cela semble assez bien fonctionner pour moi.

Je distingue les demandes non demandées des demandes répétées à l'aide des références partagées, je vais vous donner un exemple d'utilisation.

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

Voici la méthode à suivre lorsque la boîte de dialogue d’autorisation a été affichée pour la première fois, lorsque l’utilisateur a coché la case «Ne plus demander» et que l’autorisation est refusée directement après que cette option a été cochée. résulte dans onRequestPermissionsResult. Appelez la méthode checkPermission () si nécessaire.

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);
    }
}
1
Yogesh Pareek

Après avoir essayé toute la réponse ici et un autre poste sur Internet. Je suis venu pour savoir que je devais utiliser une __dependanceisLocationPermissionDialogShown partagée (par défaut, false) et que tout fonctionnait comme prévu. 

  1. Si la première fois demandé la permission. Dans ce cas, shouldShowRequestPermissionRationale renvoie false et isLocationPermissionDialogShown également false
  2. Deuxième fois, shouldShowRequestPermissionRationale renvoie true et, tout en affichant le dialogue, définissons isLocationPermissionDialogShown sur true. et lorsque nous vérifions l'état, les deux seront true 
  3. Chaque fois que jamais plus jamais coché est cochée shouldShowRequestPermissionRationale retourne true et isLocationPermissionDialogShown retourne true
  4. Si jamais demander à nouveau coché shouldShowRequestPermissionRationale retourne false et isLocationPermissionDialogShown retourne true. C'est ce dont nous avons besoin.

S'il vous plaît vérifier exemple de travail.

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

}

J'espère que cela aidera.

1
Natwar Singh

SI FINALEMENT, MON TEMPS EST VENU DE RÉPONDRE À UNE QUESTION DE COMMONSWARE


Flux d'activité: -

1. Lorsque l'utilisateur clique sur "refuser l'autorisation" pour la première fois, je vais afficher la boîte de dialogue de justification pour expliquer la nécessité de l'autorisation. Ensuite, si l'utilisateur clique sur le bouton "annuler" de la boîte de dialogue de justification, je vais afficher un toast affichant le message "Veuillez donner la permission d'obtenir l'emplacement".

Après que lorsque l'utilisateur clique sur refuser l'autorisation (ne plus demander) dans la boîte de dialogue des autorisations, je vais afficher un message "Merci de donner l'autorisation de localisation à partir des paramètres de l'application". Notez que j'ai ajouté les mots "depuis les paramètres de l'application" car l'utilisateur a coché la case "ne plus demander". 

À partir de maintenant, la boîte de dialogue d’autorisation ne sera pas affichée. le dialogue de justification ne sera pas affiché.

La clé ici est donc que si le dialogue d'autorisation et le dialogue de justification ne sont pas affichés, cela signifie que l'utilisateur a coché la case "ne plus demander".

Le code:-

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

Vérifiez ce référentiel: https://github.com/debChowdhury/PermissionHelperEasy


Easy peasy


0
debo.stackoverflow

Vous pouvez regarder ici - il y a un organigramme qui explique assez bien le processus. Il explique également quand vous devez appeler shouldShowRequestPermissionRationale() et quand il renvoie true.

En gros, selon la documentation d'Android, vous devriez toujours demander l'autorisation si vous ne l'avez pas (Android renverra automatiquement DENIED dans le rappel si l'utilisateur dit de ne plus jamais demander) et vous devriez afficher un court message si l'utilisateur a déjà refusé une fois dans le passé mais vous n'avez pas coché l'option «ne plus jamais demander».

0
Ronnie