Usar requestPermissions y onRequestPermissionsResult para pedir permisos en Android
A partir de la versión 6 de Android, los permisos “riesgosos” deben ser requeridos en tiempo de ejecución y no poniéndolos simplemente en el manifiesto de nuestra app. Citando a Android Developers:
En aquellas de tus apps que estén orientadas a Android 6.0 (nivel de API 23) o versiones posteriores, asegúrate de comprobar y solicitar los permisos en tiempo de ejecución. Para determinar si se concedió un permiso a tu app, llama al nuevo método checkSelfPermission(). Para solicitar un permiso, llama al nuevo método requestPermissions(). Incluso cuando tu app no tenga como objetivo Android 6.0 (nivel de API 23), debes probarla de acuerdo con el nuevo modelo de permisos.
Para muchas personas esto es un poco complejo y enredado, por ello es que hoy vengo a explicar cómo funcionan los permisos a partir de Android 6 y dar algunos ejemplos.
Aplicación de prueba
Para reforzar todo esto he creado una aplicación de ejemplo que pide permiso para acceder a la cámara y al almacenamiento externo al hacer click en determinado botón.
En los dispositivos con sistema operativo Android versión 6 o superior, se mostrará la petición de permiso en tiempo de ejecución:
En cambio, para los dispositivos con una versión anterior, se pedirán en tiempo de instalación:
Además de que los permisos estarán concedidos desde que se abre la app:
Código fuente y descargas
Puedes explorar el código fuente en GitHub, aunque la parte importante es el manifiesto y la actividad principal.
Todo eso que se ve en el código lo voy a explicar, pero ahí dejo la prueba de que esto funciona.
Para obtener la aplicación ve a la sección de releases y elige descargar el archivo APK, o clona el repositorio y compila la app por ti mismo.
Un poco de lectura sobre la seguridad
Como desarrolladores, nos vamos a quejar al inicio; pero después de entender esto vamos a ver por qué hace falta.
Hace tiempo, los permisos se pedían al instalar la aplicación. El sistema te indicaba que, por ejemplo, dicha app iba a acceder a la cámara y a tu ubicación. Tú como usuario lo leías pero no sabías en qué momento iba a usar esos recursos, o a veces ni lo leías y aceptabas.
Esto daba paso a muchos ataques que no puedo escribir ahora, pero se me ocurre uno: desarrollar una aplicación que tenga acceso a tu ubicación y al envío de SMS‘s; me prestas tu teléfono un momento, la instalo, acepto los permisos y te lo devuelvo.
Tú nunca sabrás qué hice con él, además de que puedo esconder la app y hacer que se ejecute en segundo plano; para más tarde espiarte y enviarme todo por SMS.
Además, estamos viendo esto del lado del informático. ¿Qué pasa con el usuario final? él no es un experto en sistemas, ni seguridad, ni programación.
Por ello es que, para hacer más seguro todo el entorno, Android introduce estas solicitudes de permisos que ahora se piden en tiempo de ejecución, y no en tiempo de instalación.
Sobre el manifiesto o AndroidManifest.xml
Recuerda que aunque los permisos van a ser solicitados en tiempo de ejecución, debes indicar a los mismos en el AndroidManifest.xml forzosamente; si no, los permisos serán denegados sin pedirle interacción al usuario.
Los paquetes que debemos importar
Para poder solicitar permisos y comprobar a los mismos, además de evitar comprobar la versión de Android para ver si es necesario pedir permisos en tiempo de ejecución son android.support.v4.content.ContextCompat
y android.content.pm.PackageManager
.
Te digo de nuevo, ve al código fuente para ver cómo se incluyen.
Los permisos en Android
Para ver si ya tenemos un permiso, se llama al método checkSelfPermission
de ContextCompat
, más adelante veremos sobre esto pero ahora un poco de teoría. Los permisos son concedidos de 2 maneras:
- Cuando solicitamos el permiso y el usuario lo concede
- Cuando el usuario va a los detalles de la app y modifica los permisos
Por ello es que no en todas las ocasiones vamos a necesitar pedir permisos, pues el usuario igualmente podría haberlos concedido desde antes.
Esto es un arma de dos filos (¿o cómo era?) porque igualmente el usuario puede ir a ese apartado y denegar los permisos. Por eso es que siempre debemos verificar los permisos cuando se va a realizar una acción que los requiera, aunque ya los hayan concedido.
Comprobar permisos
Ahora sí, para comprobar permisos llamamos a ContextCompat.checkSelfPermission
, que recibe 2 argumentos:
- El contexto
- Una cadena indicando el permiso que se comprueba; debe ser tomada de
Manifest.permission
.
Devuelve un entero que puede ser PackageManager.PERMISSION_GRANTED
o PackageManager.PERMISSION_DENIED
.
En caso de que el usuario haya concedido el permiso anteriormente o estemos en una versión de Android menor al 6, entonces el resultado será PERMISSION_GRANTED
.
Por otro lado, si es PERMISSION_DENIED
deberíamos pedir el permiso o poner una bandera en false
, para desactivar futuras características que requieren el permiso.
Aquí un ejemplo de comprobación de permiso:
// Recuerda: MainActivity es el nombre de tu actividad
int estadoDePermiso = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA);
if (estadoDePermiso == PackageManager.PERMISSION_GRANTED) {
// Aquí el usuario dio permisos para acceder a la cámara
} else {
// Si no, entonces pedimos permisos...
}
Lo que se comprueba es el permiso de acceder a la cámara.
Pedir permisos en Android
Para pedir un permiso, llamamos a requestPermissions
. Si en el ejemplo anterior el resultado no fuera PERMISSION_GRANTED
entonces llamaríamos a este método.
Por cierto, para pedir cada permiso debemos definir unos números identificadores. Son números enteros que nosotros inventamos; no deben repetirse y funcionan para cuando el sistema nos llama en onRequestPermissionsResult
.
Veamos un ejemplo para pedir el permiso de acceder a la cámara. En la parte de arriba definimos el código:
private static final int CODIGO_PERMISOS_CAMARA = 1;
En este caso le doy el valor de 1, si hubiera más permisos se pondrían con otros nombres de constantes. Como decía, el código para pedir permisos es:
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.CAMERA},
CODIGO_PERMISOS_CAMARA);
El método requestPermissions
recibe:
- El contexto
- Un arreglo de permisos que solicitamos
- El código de solicitud definido por nosotros
Aquí igualmente podríamos pedir más permisos, pero no lo haremos porque una buena práctica es pedir cada permiso por separado cuando se vaya a usar.
Al llamar este método no se sabe si el usuario denegó o concedió, es decir, el método no devuelve algo que nos importe. En su lugar, muestra al usuario el diálogo que pide permiso:
Después de que el usuario rechace o permita cada permiso (en este caso solamente es uno) se llamará al método onRequestPermissionsResult que veremos más abajo.
Nota: todo eso está en vivo dentro de la actividad principal.
Sobrescribir onRequestPermissionsResult
Este método será llamado cuando el usuario termine de aceptar o denegar los permisos, y se le pasan algunos argumentos en el siguiente orden:
requestCode
: el código único que le pasamos al pedirle permisos, es un enteropermissions
: los permisos que solicitamos en un arreglo de cadenasgrantResults
: un arreglo de enteros que pueden serPERMISSION_GRANTED
oPERMISSION_DENIED
Debemos sobrescribir el método y comparar el código de petición, o sea el requestCode
. Igualmente debemos verificar si el permiso fue dado o denegado. Aquí un ejemplo de código para el permiso de la cámara:
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case CODIGO_PERMISOS_CAMARA:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
permisoDeCamaraConcedido();
} else {
permisoDeCamaraDenegado();
}
break;
// Aquí más casos dependiendo de los permisos
// case OTRO_CODIGO_DE_PERMISOS...
}
}
El método puede devolver arreglos vacíos a veces, cuando el usuario interrumpe la solicitud. Por ello es que comprobamos que su length sea mayor a 0 (que tenga elementos) y que en su primera posición (pues solamente pedimos un permiso a la vez) sea PERMISSION_GRANTED
.
En caso de que sea denegado, podemos seguir pidiendo el permiso. Android recomienda que antes de solicitar el permiso se explique por qué se solicita, para que el usuario no desconfíe.
Conclusión
De esta manera pudimos comprobar si tenemos permisos, además de solicitarlos en cualquier versión del sistema operativo Android; ya sea en los que se requieren en tiempo de ejecución o en los que se requieren al instalar.
Te invito y animo a ver más tutoriales sobre Android, Java o Seguridad.
Por cierto, recuerda que todo el código fuente está en mi GitHub.