Services Google Play et autorisations d'exécution

Depuis Android 6.0 Marshmallow, Android utilise un modèle d'autorisations qui simplifie le processus d'installation et de mise à jour automatique des applications. Les autorisations sont demandées au moment de l'exécution plutôt qu'avant l'installation de l'application. De plus, les utilisateurs peuvent choisir de refuser des autorisations spécifiques. Pour offrir cette flexibilité aux utilisateurs, vous devez vous assurer que votre application se comporte comme prévu lorsqu'un utilisateur active ou désactive une autorisation spécifique.

Les services Google Play disposent eux-mêmes d'autorisations d'exécution que les utilisateurs peuvent choisir de refuser indépendamment des autorisations spécifiquement demandées par votre application. Les services Google Play obtiennent automatiquement toutes les autorisations nécessaires pour prendre en charge leurs API. Toutefois, votre application doit toujours vérifier et demander les autorisations d'exécution si nécessaire, et gérer de manière appropriée les erreurs lorsqu'un utilisateur refuse aux services Google Play une autorisation requise pour une API utilisée par votre application.

Il est recommandé de gérer les attentes de l'utilisateur lors de la définition des autorisations dont l'environnement d'exécution peut avoir besoin. Les bonnes pratiques suivantes vous aideront à éviter d'éventuels problèmes.

Conditions préalables

Vous devrez déclarer les autorisations dans votre fichier AndroidManifest.xml. Exemple :

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

Consignes

Vérifier les autorisations avant d'appeler des API

Une fois que vous avez déclaré les API que vous souhaitez utiliser dans votre fichier AndroidManifest.xml, assurez-vous de disposer de l'autorisation requise avant d'appeler une API. Pour ce faire, utilisez la méthode checkSelfPermission de ActivityCompat ou ContextCompat.

Si l'appel renvoie la valeur "false", cela signifie que les autorisations ne sont pas accordées. Vous devez donc utiliser requestPermissions pour les demander. La réponse est renvoyée dans un rappel, que vous verrez à l'étape suivante.

Exemple :

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
  // Check Permissions Now
  ActivityCompat.requestPermissions(this,
      new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
      REQUEST_LOCATION);
} else {
  // permission has been granted, continue as usual
  Task<Location> locationResult = LocationServices
      .getFusedLocationProviderClient(this /** Context */)
      .getLastLocation();
}

Implémenter le rappel d'autorisation de la requête

Si l'autorisation dont votre application a besoin n'a pas été accordée par l'utilisateur, la méthode requestPermissions doit être appelée pour demander à l'utilisateur de l'accorder. La réponse de l'utilisateur est capturée dans le rappel onRequestPermissionsResult. Votre application doit implémenter cela et toujours vérifier les valeurs de retour, car la requête peut être refusée ou annulée. Vous pouvez également demander et vérifier plusieurs autorisations à la fois. L'exemple suivant ne vérifie qu'une seule autorisation.

public void onRequestPermissionsResult(int requestCode,
                                       String[] permissions,
                                       int[] grantResults) {
    if (requestCode == REQUEST_LOCATION) {
        if(grantResults.length == 1
           && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // We can now safely use the API we requested access to
            Task<Location> locationResult = LocationServices
                .getFusedLocationProviderClient(this /** Context */)
                .getLastLocation();
        } else {
            // Permission was denied or request was cancelled
        }
    }
}

Afficher la justification de l'autorisation

Si les autorisations demandées par votre application sont nécessaires pour les fonctionnalités de base de l'application et que l'utilisateur a déjà refusé la demande d'autorisation, votre application doit afficher une explication supplémentaire avant de demander à nouveau l'autorisation. Les utilisateurs sont plus susceptibles d'accorder des autorisations s'ils comprennent pourquoi elles sont nécessaires et quel est leur intérêt immédiat.

Dans ce cas, avant l'appel requestPermissions, vous devez appeler shouldShowRequestPermissionRationale. Si la valeur "true" est renvoyée, vous devez créer une interface utilisateur pour afficher le contexte supplémentaire de l'autorisation.

Par exemple, votre code peut se présenter comme suit:

if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
    != PackageManager.PERMISSION_GRANTED) {
    // Check Permissions Now
    private static final int REQUEST_LOCATION = 2;

    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.ACCESS_FINE_LOCATION)) {
        // Display UI and wait for user interaction
    } else {
        ActivityCompat.requestPermissions(
            this, new String[]{Manifest.permission.LOCATION_FINE},
            ACCESS_FINE_LOCATION);
    }
} else {
    // permission has been granted, continue as usual
    Task<Location> locationResult = LocationServices
        .getFusedLocationProviderClient(this /** Context */)
        .getLastLocation();
}

Gérer les échecs de connexion

Si votre application utilise le GoogleApiClient obsolète, lorsque vous appelez connect(), les services Google Play vérifient qu'elle dispose de toutes les autorisations nécessaires. connect() échoue lorsqu'il manque des groupes d'autorisations nécessaires aux services Google Play.

Si l'appel de connect() échoue, assurez-vous que votre application gère correctement l'échec de connexion. Si les services Google Play ne disposent pas des autorisations nécessaires, vous pouvez appeler startResolutionForResult() pour lancer le parcours utilisateur et les corriger.

Exemple :

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (mResolvingError) {
        // Already attempting to resolve an error.
        return;
    } else if (result.hasResolution()) {
        try {
            mResolvingError = true;
            result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
        } catch (SendIntentException e) {
            // There was an error with the resolution intent. Try again.
            mGoogleApiClient.connect();
        }
    } else {
        // Show dialog using GooglePlayServicesUtil.getErrorDialog()
        showErrorDialog(result.getErrorCode());
        mResolvingError = true;
    }
}

Les nouveaux appels d'API basés sur GoogleApi affichent automatiquement une boîte de dialogue (si le client est instancié avec Activity) ou une notification de la barre d'état système (si le client est instancié avec Context) sur laquelle l'utilisateur peut appuyer pour lancer l'intent de résolution des autorisations. Les appels seront mis en file d'attente et de nouvelles tentatives seront effectuées une fois l'autorisation accordée.