Servicios de Google Play y permisos de tiempo de ejecución

A partir de Android 6.0 Marshmallow, modelo de permisos que optimiza la instalación y de actualización automática. Los permisos se solicitan durante el tiempo de ejecución, no antes. instalación de la app. Además, los usuarios pueden rechazar permisos específicos. Para brindarles a los usuarios esta flexibilidad, debes asegurarte de que tu app se comporte de se espera cuando un usuario habilita o inhabilita un permiso específico.

Los Servicios de Google Play tienen permisos de tiempo de ejecución que los usuarios pueden elegir rechazar por separado de los permisos solicitados específicamente por su y mantener la integridad de su aplicación. Servicios de Google Play obtiene automáticamente todos los permisos que necesita para admitir sus APIs. Sin embargo, tu app debe verificar y solicitar el tiempo de ejecución permisos según sea necesario y manejar adecuadamente los errores en los casos en que un usuario rechazó a los Servicios de Google Play un permiso necesario para una API que usa tu app.

Es una buena práctica administrar las expectativas del usuario al establecer permisos que que puede requerir el entorno de ejecución. Las siguientes prácticas recomendadas te ayudarán a evitar posibles problemas.

Requisitos previos

Deberás declarar los permisos en tu archivo AndroidManifest.xml. Por ejemplo:

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

Lineamientos

Verifica los permisos antes de llamar a las APIs

Una vez que hayas declarado las APIs que quieres usar en tu AndroidManifest.xml, asegúrate de tener el permiso necesario antes de llamar a una API. Esto se puede hacer con el método checkSelfPermission. de ActivityCompat o ContextCompat.

Si la llamada muestra el valor "false", significa que no se otorgaron los permisos debes usar requestPermissions para solicitarlas. La respuesta a esto es en una devolución de llamada que verás en el siguiente paso.

Por ejemplo:

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

Cómo implementar la devolución de llamada de solicitud de permiso

Si el usuario no otorgó el permiso que necesita tu aplicación, el Debes llamar al método requestPermissions para preguntarle a la usuario los otorgue. La respuesta del usuario se captura en el Devolución de llamada onRequestPermissionsResult. Tu app debe implementar esto y siempre comprobar los valores que se devuelven, ya que la solicitud podría rechazadas o canceladas. También puedes solicitar y verificar varios permisos en una vez, el siguiente ejemplo solo verifica un único permiso.

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

Muestra la justificación del permiso

Si los permisos que solicita tu aplicación son necesarios para las funciones principales de la app y el usuario rechazó la solicitud de permiso anteriormente, tu app debe mostrar una explicación adicional antes de volver a solicitar el permiso. Usuarios tienen más probabilidades de otorgar permisos cuando comprenden el motivo del permiso y el beneficio inmediato para ellos.

En este caso, antes del requestPermissions que realiza la llamada, debes llamar shouldShowRequestPermissionRationale Si devuelve true, debes crear alguna IU para mostrar contexto adicional para el permiso.

Por ejemplo, tu código podría verse de la siguiente manera:

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

Soluciona las fallas de conexión

Si tu app usa el elemento GoogleApiClient obsoleto, cuando llames connect(), Servicios de Google Play valida que cuente con todos los elementos los permisos necesarios. connect() falla cuando algún grupo de permisos de los servicios de Google Play.

Si la llamada a connect() falla, asegúrate de que tu app controle error de conexión correctamente. Si los Servicios de Google Play no tiene permisos, puedes invocar a startResolutionForResult() para iniciar el flujo del usuario para solucionarlos.

Por ejemplo:

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

Las llamadas a la API más nuevas basadas en GoogleApi mostrarán automáticamente un diálogo (si se crea una instancia del cliente con una Activity) o con una notificación de la bandeja del sistema (si se crea una instancia del cliente con un Context) que el usuario puede presionar para iniciar de resolución de permisos. Las llamadas se pondrán en cola y se reintentarán cuando permiso.