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

Desde o Android 6.0 Marshmallow, o Android usa um modelo de permissões que simplifica o processo de instalação de apps e atualização automática. As permissões são solicitadas durante a execução, e não antes da instalação do app. Além disso, os usuários podem optar por 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 recusar separadamente das permissões solicitadas especificamente pelo aplicativo. 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 a permissão necessária para uma API usada pelo app.

Recomendamos gerenciar as expectativas do usuário ao definir as permissões necessárias para o ambiente de execução. As práticas recomendadas a seguir ajudarão você a evitar possíveis problemas.

Pré-requisitos

É necessário 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ê precisará usar requestPermissions para solicitá-las. A resposta é retornada em um callback, que você verá na próxima etapa.

Exemplo:

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

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

Se a permissão necessária para o app não tiver sido concedida pelo usuário, o método requestPermissions precisará ser chamado para solicitar que o usuário a conceda. A resposta do usuário é capturada no callback onRequestPermissionsResult. O 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.

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

Mostrar a lógica da permissão

Se as permissões solicitadas pelo app forem necessárias para os recursos principais do app e o usuário tiver negado a solicitação de permissão, o app precisará mostrar uma explicação adicional antes de solicitar a permissão novamente. É mais provável que os usuários concedam 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 ela retornar "true", crie alguma interface para mostrar mais contexto da permissão.

Por exemplo, seu código pode ficar assim:

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

Gerenciar falhas de conexão

Se o app usar o GoogleApiClient descontinuado, quando você chamar connect(), o Google Play Services vai validar se ele tem todas as permissões necessárias. A connect() falha quando algum grupo de permissões necessário para o Google Play Services está ausente.

Se a chamada para connect() falhar, verifique se o app processa a falha de conexão corretamente. Se o Google Play Services não tiver permissões, você poderá invocar startResolutionForResult() para iniciar o fluxo do usuário para corrigi-las.

Exemplo:

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

As chamadas de API mais recentes baseadas em GoogleApi exibem automaticamente uma caixa de diálogo (se o cliente for instanciado com um Activity) ou uma notificação da bandeja do sistema (se o cliente for instanciado com um Context). O usuário poderá tocar para iniciar a intent de resolução de permissões. As chamadas serão enfileiradas e repetidas quando a permissão for concedida.