Google Play Services e permissões de execução

Desde o Android 6.0 Marshmallow, o Android usa um modelo de permissões que otimiza o processo de instalação e atualização automática de apps. As permissões são solicitadas no tempo de execução, e não antes da instalação do app. Além disso, os usuários podem negar permissões específicas. Para oferecer essa flexibilidade aos usuários, verifique se o app se comporta como esperado quando um usuário ativa ou desativa uma permissão específica.

O Google Play Services tem permissões de execução que os usuários podem negar separadamente daquelas especificamente solicitadas pelo app. O Google Play Services recebe automaticamente todas as permissões necessárias para oferecer suporte às APIs. No entanto, o app ainda precisa verificar e solicitar permissões de execução conforme necessário e processar erros adequadamente nos casos em que um usuário negou ao Google Play Services uma permissão necessária para uma API usada pelo app.

É recomendável gerenciar as expectativas do usuário ao definir permissões que o tempo de execução pode exigir. As práticas recomendadas a seguir ajudam a evitar possíveis problemas.

Pré-requisitos

Você precisa declarar as permissões no arquivo AndroidManifest.xml. Exemplo:

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

Diretrizes

Verificar permissões antes de chamar APIs

Depois de declarar as APIs que você quer usar no arquivo AndroidManifest.xml, verifique se você tem a permissão necessária antes de chamar uma API. Isso pode ser feito usando o método checkSelfPermission de ActivityCompat ou ContextCompat.

Se a chamada retornar "false", isso significa que as permissões não foram concedidas e você deve usar requestPermissions para solicitá-las. A resposta a isso é retornada em uma callback, que você vai ver na próxima etapa.

Exemplo:

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

Implementar o callback de solicitação de permissão

Se a permissão necessária para o app não tiver sido concedida pelo usuário, chame o método requestPermissions para pedir que ele conceda. A resposta do usuário é capturada no callback onRequestPermissionsResult. Seu app precisa implementar isso e sempre verificar os valores de retorno, porque a solicitação pode ser negada ou cancelada. Também é possível solicitar e verificar várias permissões de uma só vez. O exemplo a seguir verifica apenas uma permissão.

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

Mostrar a justificativa da permissão

Se as permissões solicitadas pelo app forem necessárias para os recursos principais dele e o usuário tiver negado a solicitação anteriormente, o app deverá mostrar uma explicação adicional antes de solicitar a permissão novamente. Os usuários têm mais probabilidade de conceder permissões quando entendem por que elas são necessárias e o benefício imediato para eles.

Nesse caso, antes de chamar requestPermissions, chame shouldShowRequestPermissionRationale. Se ele retornar verdadeiro, crie uma interface para mostrar mais contexto sobre a permissão.

Por exemplo, seu código pode ser assim:

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

As chamadas de API dos Serviços do Google Play vão mostrar automaticamente uma caixa de diálogo (se o cliente for instanciado com um Activity) ou uma notificação na bandeja do sistema (se o cliente for instanciado com um Context) que o usuário pode tocar para iniciar a intent de resolução de permissões. As chamadas serão enfileiradas e repetidas quando a permissão for concedida.