Desde Android 6.0 Marshmallow, Android usa un modelo de permisos que simplifica el proceso de instalación y actualización automática de apps. Los permisos se solicitan durante el tiempo de ejecución, en lugar de antes de la instalación de la app. Además, los usuarios pueden optar por rechazar permisos específicos. Para brindarles a los usuarios esta flexibilidad, debes asegurarte de que tu app se comporte como 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 rechazar por separado de los permisos que solicita específicamente tu app. Los Servicios de Google Play obtienen automáticamente todos los permisos que necesitan para admitir sus APIs. Sin embargo, tu app debe seguir verificando y solicitando permisos de tiempo de ejecución según sea necesario, y controlar los errores de forma adecuada en los casos en que un usuario haya denegado a los Servicios de Google Play un permiso requerido para una API que usa tu app.
Es una buena práctica administrar las expectativas del usuario cuando se configuran permisos que el tiempo de ejecución puede requerir. Las siguientes prácticas recomendadas te ayudarán a evitar posibles problemas.
Requisitos previos
Deberás declarar 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 deseas usar en tu archivo AndroidManifest.xml
, verifica que tengas el permiso requerido antes de llamar a una API. Esto se puede hacer con el método checkSelfPermission
de ActivityCompat
o ContextCompat
.
Si la llamada devuelve un valor falso, significa que no se otorgaron los permisos y debes usar requestPermissions
para solicitarlos. La respuesta a esto se devuelve en una devolución de llamada que verás en el siguiente paso.
Por ejemplo:
Kotlin
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Request Permissions Now ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION_CODE) } else { // permission has been granted, continue as usual val locationResult = LocationServices .getFusedLocationProviderClient(this /* Context */) .lastLocation }
Java
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Request Permissions Now ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_LOCATION_PERMISSION_CODE); } else { // permission has been granted, continue as usual TasklocationResult = LocationServices .getFusedLocationProviderClient(this /** Context */) .getLastLocation(); }
Implementa la devolución de llamada de solicitud de permiso
Si el usuario no otorgó el permiso que necesita tu app, se debe llamar al método requestPermissions
para solicitarle al usuario que lo otorgue. La respuesta del usuario se captura en la devolución de llamada onRequestPermissionsResult
. Tu app debe implementar esto y siempre verificar los valores que se muestran, ya que la solicitud podría rechazarse o cancelarse. También puedes solicitar y verificar varios permisos a la vez. El siguiente ejemplo solo verifica un permiso.
Kotlin
fun onRequestPermissionsResult( requestCode: Int, permissions: Array, grantResults: IntArray ) { if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) { if (grantResults.singleOrNull() == PackageManager.PERMISSION_GRANTED) { // We can now safely use the API we requested access to val locationResult: Task = LocationServices .getFusedLocationProviderClient(this /* Context */) .lastLocation // Request the last known location. } else { // Permission was denied or request was cancelled } } }
Java
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_LOCATION_PERMISSION_CODE) { if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // We can now safely use the API we requested access to TasklocationResult = LocationServices .getFusedLocationProviderClient(this /** Context */) .getLastLocation(); // Request the last known location. } else { // Permission was denied or request was cancelled } } }
Mostrar la justificación del permiso
Si los permisos que solicita tu app 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. Es más probable que los usuarios otorguen permisos cuando comprendan por qué se necesitan y el beneficio inmediato que les brindan.
En este caso, antes de llamar a requestPermissions
, debes llamar a shouldShowRequestPermissionRationale
. Si devuelve verdadero, debes crear una IU para mostrar contexto adicional sobre el permiso.
Por ejemplo, tu código podría verse así:
Kotlin
private const val REQUEST_LOCATION_PERMISSION_CODE = 2 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Check Permissions Now if ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.ACCESS_FINE_LOCATION ) { // Display UI and wait for user interaction } else { ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION_CODE) } } else { // Permission has already been granted, continue as usual val locationResult: Task= LocationServices .getFusedLocationProviderClient(this /* Context */) .lastLocation }
Java
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Check Permissions Now private static final int REQUEST_LOCATION_PERMISSION_CODE = 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}, REQUEST_LOCATION_PERMISSION_CODE); } } else { // permission has been granted, continue as usual TasklocationResult = LocationServices .getFusedLocationProviderClient(this /** Context */) .getLastLocation(); }
Las llamadas a la API de los Servicios de Google Play mostrarán automáticamente un diálogo (si el cliente se instancia con un Activity
) o una notificación en la bandeja del sistema (si el cliente se instancia con un Context
) en el que el usuario puede presionar para iniciar el intent de resolución de permisos. Las llamadas se pondrán en cola y se volverán a intentar una vez que se otorgue el permiso.