Google Play Services e autorizzazioni di runtime

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

I servizi Google Play hanno autorizzazioni di runtime che gli utenti possono scegliere di negare separatamente da quelle richieste specificamente dalla tua app. I servizi Google Play ottengono automaticamente tutte le autorizzazioni necessarie per supportare le proprie API. Tuttavia, la tua app deve comunque controllare e richiedere le autorizzazioni di runtime in base alle necessità e gestire gli errori in modo appropriato 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 delle autorizzazioni che il runtime potrebbe richiedere. 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

Verificare le autorizzazioni prima di chiamare le API

Dopo aver dichiarato le API che vuoi utilizzare nel file AndroidManifest.xml, verifica di disporre dell'autorizzazione richiesta prima di chiamare un'API. Puoi farlo utilizzando il metodo checkSelfPermission di ActivityCompat o ContextCompat.

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

Ad esempio:

Kotlin

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            // Request Permissions Now
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
    } else {
        // permission has been granted, continue as usual
        val locationResult = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Java

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

Implementa il callback di richiesta di autorizzazione

Se l'autorizzazione necessaria per la tua app non è stata concessa dall'utente, devi chiamare il metodo requestPermissions per chiedere all'utente di concederla. La risposta dell'utente viene acquisita nel callback onRequestPermissionsResult. La tua app deve implementare questo e controllare sempre i valori restituiti perché la richiesta potrebbe essere rifiutata o annullata. Puoi anche richiedere e verificare più autorizzazioni contemporaneamente. L'esempio seguente verifica solo una singola autorizzazione.

Kotlin

    fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array,
        grantResults: IntArray
    ) {
        if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) {
            if (grantResults.singleOrNull() == PackageManager.PERMISSION_GRANTED) {
               // We can now safely use the API we requested access to
               val locationResult: Task = LocationServices
                    .getFusedLocationProviderClient(this /* Context */)
                    .lastLocation // Request the last known location.
            } else {
                // Permission was denied or request was cancelled
            }
        }
    }

Java

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

Mostrare la motivazione dell'autorizzazione

Se le autorizzazioni richieste dalla tua app sono necessarie per le funzionalità principali dell'app e l'utente ha precedentemente negato la richiesta di autorizzazione, la tua app deve mostrare una spiegazione aggiuntiva prima di richiedere nuovamente l'autorizzazione. Gli utenti sono più propensi a concedere le autorizzazioni quando comprendono il motivo per cui sono necessarie e il vantaggio immediato che ne traggono.

In questo caso, prima di chiamare requestPermissions, devi chiamare shouldShowRequestPermissionRationale. Se restituisce true, devi creare un'interfaccia utente per visualizzare il contesto aggiuntivo per l'autorizzazione.

Ad esempio, il codice potrebbe avere il seguente aspetto:

Kotlin

    private const val REQUEST_LOCATION_PERMISSION_CODE = 2
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        if ActivityCompat.shouldShowRequestPermissionRationale(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) {
            // Display UI and wait for user interaction
        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                REQUEST_LOCATION_PERMISSION_CODE)
        }
    } else {
        // Permission has already been granted, continue as usual
        val locationResult: Task = LocationServices
            .getFusedLocationProviderClient(this /* Context */)
            .lastLocation
    }

Java

    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
        // Check Permissions Now
        private static final int REQUEST_LOCATION_PERMISSION_CODE = 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},
                REQUEST_LOCATION_PERMISSION_CODE);
        }
    } else {
        // permission has been granted, continue as usual
        Task locationResult = LocationServices
            .getFusedLocationProviderClient(this /** Context */)
            .getLastLocation();
    }

Le chiamate API di Google Play Services mostreranno automaticamente una finestra di dialogo (se il client viene istanziato con un Activity) o una notifica nella barra delle applicazioni (se il client viene istanziato con un Context) che l'utente può toccare per avviare l'intent di risoluzione delle autorizzazioni. Le chiamate verranno messe in coda e riprovate una volta concesso il permesso.