Google Play Services e autorizzazioni di runtime

A partire da Android 6.0 Marshmallow, Android utilizza una modello di autorizzazioni che semplifica l'installazione delle app e processo di aggiornamento automatico. Le autorizzazioni vengono richieste in fase di runtime anziché prima l'installazione di app. Inoltre, gli utenti possono scegliere di negare autorizzazioni specifiche. Per offrire agli utenti questa flessibilità, devi assicurarti che l'app si comporti come previsto quando un utente abilita o disabilita un'autorizzazione specifica.

Google Play Services dispone di autorizzazioni di runtime che gli utenti possono scegliere negare separatamente dalle autorizzazioni specificamente richieste un'applicazione. Google Play Services ottiene automaticamente tutte le autorizzazioni di cui ha bisogno per supportare le sue API. Tuttavia, la tua app dovrebbe comunque controllare e richiedere il runtime autorizzazioni necessarie e gestire in modo appropriato gli errori nei casi in cui un utente ha negato a Google Play Services un'autorizzazione richiesta per un'API utilizzata dalla tua app.

È buona norma gestire le aspettative dell'utente nell'impostazione di autorizzazioni che richieste dal runtime. Le seguenti best practice ti aiuteranno a evitare potenziali problemi.

Prerequisiti

Dovrai dichiarare le autorizzazioni nel file AndroidManifest.xml. Ad esempio:

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

Linee guida

Verifica le autorizzazioni prima di chiamare le API

Una volta dichiarate le API da utilizzare AndroidManifest.xml file, assicurati di disporre dell'autorizzazione richiesta prima di chiamare un'API. Questa operazione può essere eseguita utilizzando il metodo checkSelfPermission di ActivityCompat o ContextCompat.

Se la chiamata restituisce false, significa che le autorizzazioni non sono state concesse e devono utilizzare requestPermissions per richiederle. La risposta è in un callback, che vedrai nel passaggio successivo.

Ad esempio:

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

Implementare il callback di autorizzazione alla richiesta

Se l'autorizzazione necessaria alla tua app non è stata concessa dall'utente, requestPermissions deve essere chiamato per chiedere affinché l'utente possa concederle. La risposta dell'utente viene acquisita nella onRequestPermissionsResult. La tua app dovrebbe implementarlo e controllare sempre i valori restituiti perché la richiesta potrebbe essere rifiutato o annullato. Puoi anche richiedere e verificare più autorizzazioni all'indirizzo una volta: il seguente esempio verifica solo la presenza di una singola autorizzazione.

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

Mostra la motivazione dell'autorizzazione

Se le autorizzazioni richieste dalla tua app sono necessarie per le funzionalità principali del e l'utente ha precedentemente rifiutato la richiesta di autorizzazione, l'app deve mostrare una spiegazione aggiuntiva prima di richiedere nuovamente l'autorizzazione. Utenti è più probabile che conceda le autorizzazioni quando capisce il motivo e il vantaggio immediato per loro.

In questo caso, prima di chiamare requestPermissions, devi chiamare shouldShowRequestPermissionRationale Se viene restituito è necessario creare un'interfaccia utente per visualizzare un contesto aggiuntivo per autorizzazione.

Ad esempio, il codice potrebbe avere il seguente aspetto:

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

Gestire gli errori di connessione

Se la tua app utilizza la versione deprecata GoogleApiClient, quando chiami connect(), Google Play Services verifica che contenga tutte le le autorizzazioni necessarie. connect() non riesce quando un gruppo di autorizzazioni richieste da Google Play Services.

Se la chiamata al numero connect() non va a buon fine, assicurati che l'app gestisca il correttamente l'errore di connessione. Se Google Play Services non ha le autorizzazioni necessarie, puoi richiamare startResolutionForResult() per avvia la procedura per correggerli.

Ad esempio:

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

Per le chiamate API più recenti basate su GoogleApi verrà visualizzata automaticamente una finestra di dialogo (se viene creata un'istanza del client con una notifica Activity) o nella barra delle applicazioni (se viene creata un'istanza del client con un Context) che l'utente può toccare per avviare intent di risoluzione delle autorizzazioni. Le chiamate verranno accodate e tentate di nuovo dopo il sia stata concessa l'autorizzazione.