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 a instalação do app e processo de atualização automática. As permissões são solicitadas no momento da execução e não antes instalação do app. Além disso, os usuários podem optar por negar permissões específicas. Para dar aos usuários essa flexibilidade, você precisa garantir que seu aplicativo se comporte da forma esperada 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 escolher negar separadamente das permissões solicitadas pelo para o aplicativo. O Google Play Services recebe automaticamente todas as permissões necessárias para dar suporte às APIs. No entanto, o app ainda vai precisar verificar e solicitar o ambiente de execução as permissões necessárias e lidar com os erros de forma adequada nos casos em que um usuário negou ao Google Play Services uma permissão necessária para uma API usada pelo seu app.

É uma boa prática gerenciar as expectativas do usuário ao definir permissões que o ambiente de execução pode exigir. As práticas recomendadas a seguir ajudarão você a evitar possíveis problemas.

Pré-requisitos

Você vai precisar declarar permissões no arquivo AndroidManifest.xml. Exemplo:

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

Diretrizes

Verificar as permissões antes de chamar APIs

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

Se a chamada retornar "false", as permissões não foram concedidas e você precisa usar requestPermissions para solicitá-las. A resposta para isso é em um callback, que você vai conhecer 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 O método requestPermissions deve ser chamado para solicitar que o usuário conceda. A resposta do usuário é capturada no Callback onRequestPermissionsResult. Seu app precisa implementá-la e sempre verificar os valores de retorno, pois a solicitação pode ser negado ou cancelado. Você também pode solicitar e verificar várias permissões em uma vez. O exemplo a seguir verifica apenas uma única 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 justificativa da permissão

Se as permissões solicitadas pelo aplicativo forem necessárias para os recursos principais do e o usuário negou a solicitação de permissão, o app deverá exibir uma explicação adicional antes de solicitar a permissão novamente. Usuários têm mais chances de conceder permissões quando entendem o motivo delas necessário e o benefício imediato para ele.

Nesse caso, antes de chamar requestPermissions, chame shouldShowRequestPermissionRationale. Se ela retornar verdadeiro, você deve criar alguma interface para exibir contexto adicional para o 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();
}

Lidar com falhas de conexão

Caso seu app use o GoogleApiClient, que foi descontinuado, quando você chamar connect(), o Google Play Services vai validar que tem todos os as permissões necessárias. A connect() falha quando qualquer grupo de permissões necessários para o próprio Google Play Services não estão.

Se a chamada para connect() falhar, verifique se o app processa a falha na conexão corretamente. Se o Google Play Services não tiver permissões, invoque startResolutionForResult() para inicie o fluxo do usuário para corrigi-los.

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 exibirão automaticamente uma caixa de diálogo (se o cliente é instanciado com um Activity) ou notificação da bandeja do sistema (se o cliente é 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 depois que o seja concedida.