domingo, noviembre 01, 2015

Tutorial solicitar permisos en tiempo de ejecución "Run Time" en Android 6.0 Marshmallow

[ 02 de Enero de 2017]

Te recomiendo que te leas este post completo para entender cómo funciona la solicitud de permisos en Android +6.0.  Luego lee Solicitar permisos en Android en tiempo de ejecución más fácilmente en donde explico una forma re-utilizable para la solicitud de permisos. 


Uno de los grandes cambios que viene en Android 6.0 es la manera que las aplicaciones acceden a las características del teléfono, tales como, servicios de telefonía, escribir y leer contactos, datos en la memoria externa entre otros.

Según la documentación, Android separa en dos grupos los permisos: permisos normales y permisos peligrosos.

Los permisos normales, no ponen en riesgo directamente la privacidad del usuario, si tu app posee en su manifiesto permisos normales, el sistema les da acceso automáticamente. Lista de permisos normales

Los permisos peligrosos, pueden dar permiso al app acceso a datos confidenciales del usuario. Si tu app posee en su manifiesto permisos peligrosos, el usuario tiene que aprobar el permiso explícitamente. Grupos de permisos peligrosos

En fin, desde mi punto de vista es útil este cambio pero a la vez tedioso para el usuario y mas aún para el programador puesto que, el hecho de que un usuario le de permiso a tu aplicación una vez, no quiere decir que lo tendrá para siempre, el usuario puede en cualquier momento volver a revocar el permiso de tu app desde la actividad de configuración de la misma. En otras palabras, cada vez que nuestra aplicación necesite un permisos peligroso, nuestro código tiene que verificar si ésta tiene o no el permiso necesario. Acá un ejemplo de los permisos de WhatsApp, yo podría negar el permiso de contactos y la aplicación queda totalmente sin sentido. Hasta el momento WhatsApp no ha implementado completamente este nuevo sistema en Android 6.0, no vi los diálogos de permisos luego de revocar el de contactos.

De acuerdo con google hay maneras de evitar, la solicitud de permisos; un ejemplo sería  el uso de la cámara, en vez de crear tu propia lógica para tomar una foto, podemos llamar al intent de la cámara directamente y traernos la foto a nuestra app, +Nick Butcher explica esto en más detalle.


Ok, ahora a lo que vinimos, el código. Voy a demostrar el uso de los permisos en tiempo de ejecución de guardado de datos en el almacenamiento del dispositivo. pasaré por alto la parte de la interfaz gráfica a excepción de un detalle para la implementación del SnackBar.

Podemos solicitar permisos desde el Splash Screen si queremos, si es de vital importancia para que la aplicación funcione correctamente y simplemente no dejar al usuario hacer nada mas, pero este no es será caso, entonces.

Paso 1.

Usamos el método checkSelfPermission("nombre del permiso") para saber si la aplicación tiene o no el permiso, PackageManager tiene las constantes para la validación. Está bastante claro lo que hace, si la verificación es verdadera solicitamos el permiso, sino guardamos el comentario.

private void verifyPermission()
{
 //WRITE_EXTERNAL_STORAGE tiene implícito READ_EXTERNAL_STORAGE
 //porque pertenecen al mismo grupo de permisos 
 int writePermission = checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE);
 if (writePermission != PackageManager.PERMISSION_GRANTED){ 
  requestPermission();
 }
 else { 
  saveComments();
 }
}


Paso 2.

Solicitamos el permiso llamando al método requestPermission("lista de permisos", request code), en este punto podemos llamar siempre requestPermission, lo que mostrará el dialogo de solicitud varias veces pero la segunda vez que se muestra el dialogo éste trae un checkbox que dice "No volver a preguntar", si el usuario lo selecciona y niega el permiso, el dialogo no se mostrará nunca más.

Por este motivo google creó shouldShowRequestPermissionRationale() este método devuelve verdadero si ya se mostró el diálogo de permisos, si es así podemos instruir al usuario de por qué necesitamos el permiso o mostrar un mensaje en el SnackBar y asignar una acción.

El SnackBar requiere una vista donde mostrarse, para ello definimos la variable mLayout la cual se asigna en el onCreate y corresponde en este caso al linear layout principal de la actividad.




private void requestPermission() {
if(ActivityCompat.shouldShowRequestPermissionRationale(this,
   Manifest.permission.WRITE_EXTERNAL_STORAGE)) { 
       showSnackBar(); 
   }else {
     requestPermissions(
     new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_WRITE_EXTERNAL_STORAGE); 
   } 
}

private void showSnackBar() { 
    Snackbar.make(mLayout,R.string.permission_write_storage,Snackbar.LENGTH_LONG)
            .setAction(R.string.settings, new View.OnClickListener() {
                 @Override public void onClick(View view){ 
                openSettings(); 
    }}).show(); 
}

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mLayout = findViewById(R.id.linearLayoutMain);
}

Paso 3.

Procesar la respuesta del usuario, para esto, sobre escribimos el método onRequestPermissionsResult, este recibe el requestCode que asignamos cuando solicitamos el permiso, los permisos solicitados y las respuestas del usuario a las solicitudes de los permisos.

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, 
                                       int[] grantResults) 
{
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == MY_WRITE_EXTERNAL_STORAGE) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
           saveComments();
        }else {
          showSnackBar();
        }
    }
}

Al principio es un poco complicado comprender lo que se debe hacer pero luego no es tanto.

En la sección Código Fuente encontrar el código completo del ejemplo, espero sus comentario.