Solicitar Permisos en Tiempo de Ejecución

A partir de Android 6.0 (API 23) los usuarios deben conceder los permisos en tiempo de ejecución y no como estábamos acostumbrados a dar los permisos al momento de la instalación. En este artículo vamos a aprender a implementar la solicitud de permisos en tiempo de ejecución con un ejemplo practico.

La declaración de los permisos, se deben realizar si o si en el AndroidManifest.xml, tanto para los permisos normales como peligrosos.

Ejemplo solicitar permisos en el AndroidManifest.xml

Este es un ejemplo de como solicitar permisos en el AndroidManifest.xml, este archivo se encuentra en la carpeta manifests del proyecto.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
    package="com.nombre.de.tu.paquete">
    <!--Agrega todos los permisos que requiera tu app-->
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!--Fin permisos-->

<application  
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>  

Permisos normales

Para los permisos normales no es necesario, realizar ninguna implementación, basta con agregarlos en el archivo AndroidManifest.xml, acá pueden encontrar la lista de permisos normales

Permisos peligrosos

Los permisos peligrosos son aquellos que incluyen servicios que pueden ser modificados o que son privados para el usuario, estos permisos se deben agregar en el manifest y otorgarlos en tiempo de ejecución lo cual veremos a continuación. Acá pueden encontrar la lista de permisos riesgosos

Solicitar permisos en tiempo de ejecución

Para que entiendan el procedimiento, vamos a realizar la lectura de los contactos de Android, con este ejemplo deben ser capaces de solicitar cualquier permiso en tiempo de ejecución.

Nuestra app abrirá el Intent de la agenda de contactos una vez concedido el permiso, luego seleccionaremos un contacto y obtendremos el número siempre y cuando haya sido guardado como móvil.

Primero solicitamos en permiso en el AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  package="com.nombre.de.tu.paquete">
  <!--Permisos-->
  <uses-permission android:name="android.permission.READ_CONTACTS"/>
  <!--Fin permisos-->
</manifest>  

XML

Nombre del archivo activity_permisos.xml

<?xml version="1.0" encoding="utf-8"?>  
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nombre.de.tu.paquete.PermisosActivity">

    <EditText
        android:id="@+id/et_numero_contacto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintHorizontal_bias="0.0" />

    <Button
        android:id="@+id/bt_contactos"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Ver contactos"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginTop="16dp"
        app:layout_constraintTop_toBottomOf="@+id/et_numero_contacto" />
</android.support.constraint.ConstraintLayout>  

Código Android

Nombre del archivo PermisosActivity.java

//Cambia esta línea de código - nombre de tu paquete
package com.a01luisrene.tutoriales;

import android.Manifest;  
import android.content.DialogInterface;  
import android.content.Intent;  
import android.content.pm.PackageManager;  
import android.database.Cursor;  
import android.net.Uri;  
import android.os.Build;  
import android.provider.ContactsContract;  
import android.support.annotation.NonNull;  
import android.support.v4.content.ContextCompat;  
import android.support.v7.app.AlertDialog;  
import android.support.v7.app.AppCompatActivity;  
import android.os.Bundle;  
import android.view.View;  
import android.widget.Button;  
import android.widget.EditText;  
import android.widget.Toast;

import com.a01luisrene.tutoriales.R;

public class PermisosActivity extends AppCompatActivity implements View.OnClickListener {  
    //Los valores no deben repetirse
    private static final int PICK_CONTACT_REQUEST = 99;
    private static final int READ_CONTACTS_PERMISSION = 100;
    //Variables
    EditText mEtNumeroContacto;
    Button mBtVerContactos;

    //Obtener número de los contactos del phone
    Cursor contactCursor, phoneCursor;
    Uri contactoUri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permisos);

        mEtNumeroContacto = (EditText) findViewById(R.id.et_numero_contacto);
        mBtVerContactos = (Button) findViewById(R.id.bt_contactos);
        mBtVerContactos.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt_contactos:
                accederAgendaContactos();
                break;
        }
    }

    private void accederAgendaContactos(){
        //si la API 23 a mas
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            //Habilitar permisos para la version de API 23 a mas

            int verificarPermisoReadContacts = ContextCompat
                    .checkSelfPermission(this, Manifest.permission.READ_CONTACTS);
            //Verificamos si el permiso no existe
            if(verificarPermisoReadContacts != PackageManager.PERMISSION_GRANTED){
                //verifico si el usuario a rechazado el permiso anteriormente
                if(shouldShowRequestPermissionRationale(Manifest.permission.READ_CONTACTS)){
                    //Si a rechazado el permiso anteriormente muestro un mensaje
                    mostrarExplicacion();
                }else{
                    //De lo contrario carga la ventana para autorizar el permiso
                    requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, READ_CONTACTS_PERMISSION);
                }

            }else{
                //Si el permiso ya fue concedido abrimos en intent de contactos
                abrirIntentContactos();
            }

        }else{//Si la API es menor a 23 - abrimos en intent de contactos

            abrirIntentContactos();
        }
    }
    private void abrirIntentContactos(){
        Intent i = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
        startActivityForResult(i, PICK_CONTACT_REQUEST);
    }

    private void mostrarExplicacion() {
        new AlertDialog.Builder(this)
                .setTitle("Autorización")
                .setMessage("Necesito permiso para acceder a los contactos de tu dispositivo.")
                .setPositiveButton("Aceptar", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                            requestPermissions(new String[]{Manifest.permission.READ_CONTACTS}, READ_CONTACTS_PERMISSION);
                        }

                    }
                })
                .setNegativeButton("Cancelar", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                    //Mensaje acción cancelada
                    mensajeAccionCancelada();
                    }
                })
                .show();
    }

    public void mensajeAccionCancelada(){
        Toast.makeText(getApplicationContext(),
                "Haz rechazado la petición, por favor considere en aceptarla.",
                Toast.LENGTH_SHORT).show();
    }

    private void mostrarCelular(Uri uri) {
        //Cargar el valor obtenido en el campo número contacto
        mEtNumeroContacto.setText(getNumeroCelular(uri));
    }
    private String getNumeroCelular(Uri uri) {
        //Variables temporales para el id y el teléfono
        String id = null;
        String smartphone = null;

        // PRIMERA CONSULTA

        //Obtener el _ID del contacto

        contactCursor = getApplicationContext().getContentResolver().query(
                uri,
                new String[]{ContactsContract.Contacts._ID},
                null,
                null,
                null);

        if(contactCursor != null && contactCursor.getCount() > 0){

            if(contactCursor.moveToFirst()) {
                id = contactCursor.getString(0);
            }

            contactCursor.close();
        }


        //SEGUNDA CONSULTA
        /*
        Sentencia WHERE para especificar que solo deseamos
        números de telefonía móvil
         */
        String selectionArgs =
                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = ? AND " +
                        ContactsContract.CommonDataKinds.Phone.TYPE+" = " +
                        ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE;

        /*
        Obtener el número telefónico
         */
        phoneCursor = getApplicationContext().getContentResolver().query(
                ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                new String[] { ContactsContract.CommonDataKinds.Phone.NUMBER },
                selectionArgs,
                new String[] { id },
                null
        );
        if(phoneCursor != null && phoneCursor.getCount() > 0){

            if (phoneCursor.moveToFirst()) {
                smartphone = phoneCursor.getString(0);
            }

            phoneCursor.close();
        }


        return smartphone;
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case READ_CONTACTS_PERMISSION:
                //Si el permiso a sido concedido abrimos la agenda de contactos
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    accederAgendaContactos();
                } else {
                    mensajeAccionCancelada();
                }
                break;
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        //Obtener el número de teléfono
        if((resultCode == RESULT_OK) && (requestCode == PICK_CONTACT_REQUEST) && (data != null) ){
            contactoUri = data.getData();
            mostrarCelular(contactoUri);
        }
    }
}

Listo mis amigos espero que les sirva 😉

Luis Rene Mas Mas's Picture

Luis Rene Mas Mas

Hola, soy desarrollador front-end & dasarrollador móvil (Android), me encanta las tecnologías web - móvil, también administro este blog.

Trujillo - Perú @01luisrene

Únete al Blog

Obtén los últimos artículos publicados directamente en tu bandeja de entrada.

O suscríbase vía RSS con Feedly!

Comparte tu Opinión