Bienvenidos sean a este post, hoy hablaremos sobre los permisos a partir de Android 6. Antes de este sistema operativo, el usuario concedia los permisos durante la instalacion de la app, en caso de querer evitar simplemente debia evitar instalar la misma pero a partir de Android 6 nos pueden preguntar si queremos que la app acceda a ciertos elementos de nuestro dispositivo, tal como vimos en el post anterior los permisos se dividen en dos categorias: peligrosos y normales; a su vez estos se subdividen en mas categorias pero les recomiendo el post anterior para ver mas a fondo los mismos, como se puede deducir los permisos normales se suelen conceder en la instalacion pero los peligrosos no pero una vez ejecutado la primera vez nos preguntara si deseamos concederlo o no, recuerden cuando hicimos la grabadora en este post si bien no nos preguntaron tuvimos que darle permiso por medio de los ajustes del dispositivo.

Antes de continuar necesitaremos tener dos condiciones para chequear estos permisos, primero en el SDK Manager deberan tener instalado el SDK de Marshmallow como se ve en la siguiente imagen

Si no lo tienen les recomiendo este post donde explico como se agregan los SDK para poder trabajar con ellos, nuestro segundo paso sera crear (sino lo tienen) un equipo virtual para con la version de Marshmallow para poder probar nuestros permisos, como se ve en la siguiente imagen

Si necesitan instalarlo les recomiendo este post donde explico como generar una nueva, si tienen estos dos requisitos procedamos a crear a nuestra app para poder trabajar, los requisitos son estos:

  • Application Name: Permisos en MarshMallow
  • Company Domain: example.org
  • Minimum SDK API (Phone and Tablet): 14
  • Add an Activity: Basic Activity.

La API en mi caso no me deja menos que 14 pero si pueden ponerlo mas bajo traten, una vez generada nuestra app haremos algunas modificaciones, primero declararemos esta variable al comienzo de nuestra clase:

private View vista;

Este objeto o variable la usaremos para generar el link con nuestro elemento de Layout en activity_main.xml, luego modificaremos el metodo onClick() contenido dentro del metodo onCreate() de la siguiente forma:

public void onClick(View view) {
borrarLlamada();
/* Snackbar.make(view, "Replace with your own action",
Snackbar.LENGTH_LONG)
.setAction("Action", null).show(); */
}

Como pueden ver se agrego una nueva llamada a un metodo, borrarLlamada(), y comentaremos todas las lineas restantes para que el programa no las interprete, acto seguido agregaremos la siguiente linea en el metodo onCreate():

 vista = findViewById(R.id.content_main);

Esta se encargara de crear el vinculo entre nuestro codigo y el layout, finalmente vamos a agregar el metodo faltante:

    void borrarLlamada(){
        getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                "number='555555555'",null);
        Snackbar.make(vista,"Llamada borrada del registro",
                Snackbar.LENGTH_SHORT).show();
    }

En este metodo la primera linea se encarga se borrar los registros de llamadas con el numero 555555555, y en lugar de usar el metodo Toast utilizaremos el metodo Snackbar, metodo similar pero ya veremos como trabaja, solo nos resta agregar la siguiente linea en nuestro archivo de layout, activity_main.xml, para poder finalmente probarlo:

android:id="@+id/content_main"

Con todas estas modificaciones solo nos resta probarlo, si lo probamos en algun emulador con un Android menor a la version 6.0 al hacer click sobre el boton nos devolvera el siguiente mensaje

Para que funcione correctamente debemos modificar el archivo AndroidManifest.xml agregando el siguiente permiso:

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

El permiso se asigna como vinimos haciendo hasta ahora, repasen algunos de los post anteriores para ver como ubicarlo y si lo probamos podremos ver como ahora funciona correctamente

Pero si ahora lo probamos en otro equipo con Android 6.0 o superior obtendremos la siguiente falla

Esto es de lo que hablamos en el post anterior, ahora tenemos otras categorias de permisos y como este ingresa dentro de la categoria peligrosos no nos alcanza solamente con autorizarlo por medio de AndroidManifest.xml sino que debemos autorizarlo de la siguiente manera:

Primero deberemos ir a las Configuraciones (Settings) del dispositivo

Luego deberan ir hasta Aplicaciones (Apps) y seleccionarlo

Una vez seleccionado busquen la app nuestra, Permisos en Marshmallow y deben seleccionarla

Despues de seleccionada podremos ver todas las opciones, propiedades y acciones de la misma, en este caso deberemos seleccionar Permisos (Permissions)

Finalmente con esta opcion seleccionada podremos ver todos los permisos que necesita nuestra app

Como se ve en la imagen, si nosotros habilitamos esta opcion nuestra app ahora deberia funcionar de forma correcta porque habilitamos lo que necesitaba pero nosotros no podemos obligar al usuario a tener que hacer esto por cada permiso de nuestras apps, para ello veremos como podemos agregar una serie de modificaciones para que el usuario reciba un mensaje en pantalla solicitando que nos permite habilitar estos permisos para ello deberemos volver a nuestro proyecto, nuestra primera modificacion sera en el metodo borrarLlamada():

    void borrarLlamada(){
        if(ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_CALL_LOG) == 
                PackageManager.PERMISSION_GRANTED){
            getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                    "number='555555555'",null);
            Snackbar.make(vista,"Llamada borrada del registro",
                    Snackbar.LENGTH_SHORT).show();
        }
    }

Como pueden ver agregamos un condicional que por medio del metodo checkSelfPermission(), el cual primero chequea el contexo (this), luego le decimos cual es el permiso que deseamos verificar y despues por medio de PackageManager y la constante PERMISSION_GRANTED chequearemos si el permiso esta concedido o no, y en caso de estar concedido ejecutara la accion pero aun esta opcion no nos sirve porque si no esta configurado no hara nada, para ello haremos la siguiente modificacion:

    void borrarLlamada(){
        if(ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_CALL_LOG) ==
                PackageManager.PERMISSION_GRANTED){
            getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                    "number='555555555'",null);
            Snackbar.make(vista,"Llamada borrada del registro",
                    Snackbar.LENGTH_SHORT).show();
        } else {
            solicitarPermisoBorrarLlamada();
        }
    }

Ahora en caso de ser falso nos enviara a un nuevo metodo, no lo prueben porque va a generar un error, para ello ahora agregaremos el nuevo metodo:

void solicitarPermisoBorrarLlamada(){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_CALL_LOG)){
Snackbar.make(vista,"Sin el permiso administrar llamadas"
+" no puedo borrar llamadas del registro", Snackbar.
LENGTH_INDEFINITE)
.setAction("Ok", new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{ Manifest.
permission.WRITE_CALL_LOG },
SOLICITUD_PERMISO_WRITE_CALL_LOG );
}
})
.show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.WRITE_CALL_LOG },
SOLICITUD_PERMISO_WRITE_CALL_LOG);
}
}

Este metodo es sencillo nuestro primer condicional por medio del metodo entre los parentesis se chequea si es necesario notificar al usuario sobre este permiso, en caso contrario pasara directamente al bloque del else pero antes hablemos sobre este bloque. Si considera que necesita mostrar el mensaje, lo hara por medio del metodo Snackbar, luego pedira confirmacion para proceder, en caso de apretar el Ok nos aparecera la notificacion para habilitar el permiso necesario, el bloque del else hace justamente esto ultimo aparecernos el mensaje de confirmacion de permisos. Todavia no es probable probarlo solamente necesitamos hacer dos cambios mas, el primer es agregar la siguiente linea al principio de la clase:

private static final int SOLICITUD_PERMISO_WRITE_CALL_LOG = 0;

Esta es la constante antes utilizada en el metodo antes descripto pero para que el metodo anterior funcione deberemos hacer la siguiente sobrecarga para poder utilizarlo correctamente:

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String[] permisos,
                                           int[] grantResults){
        if(requestCode == SOLICITUD_PERMISO_WRITE_CALL_LOG){
            if (grantResults.length == 1 && grantResults[0] ==
                    PackageManager.PERMISSION_GRANTED) {
                borrarLlamada();
            } else {
                Snackbar.make(vista,"Sin el permiso, no puedo"
                        + "realizar la accion", Snackbar.
                        LENGTH_SHORT).show();
            }
        }
    }

Cuando solicitemos el requestPermissions() utilizare este ultimo metodo, aca chequearemos si requestCode es igual a la constante, chequearemos si realmente estan concedidos los permisos y procedera a llamar al metodo borrarLlamada() de lo contrario nos mostrara un mensaje de notificacion, antes de probarlo veamos como quedo finalmente nuestro codigo de la clase MainActivity:

 package org.example.permisosenmarshmallow;

import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.CallLog;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends AppCompatActivity {

private View vista;
private static final int SOLICITUD_PERMISO_WRITE_CALL_LOG = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vista = findViewById(R.id.content_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
borrarLlamada();
/* Snackbar.make(view, "Replace with your own action",
Snackbar.LENGTH_LONG)
.setAction("Action", null).show(); */
}
});
}

void borrarLlamada(){
if(ContextCompat.checkSelfPermission(this,
Manifest.permission.WRITE_CALL_LOG) ==
PackageManager.PERMISSION_GRANTED){
getContentResolver().delete(CallLog.Calls.CONTENT_URI,
"number='555555555'",null);
Snackbar.make(vista,"Llamada borrada del registro",
Snackbar.LENGTH_SHORT).show();
} else {
solicitarPermisoBorrarLlamada();
}
}

void solicitarPermisoBorrarLlamada(){
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.WRITE_CALL_LOG)){
Snackbar.make(vista,"Sin el permiso administrar llamadas"
+" no puedo borrar llamadas del registro", Snackbar.
LENGTH_INDEFINITE)
.setAction("Ok", new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCompat.requestPermissions(
MainActivity.this,
new String[]{ Manifest.
permission.WRITE_CALL_LOG },
SOLICITUD_PERMISO_WRITE_CALL_LOG );
}
})
.show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{ Manifest.permission.WRITE_CALL_LOG },
SOLICITUD_PERMISO_WRITE_CALL_LOG);
}
}

@Override
public void onRequestPermissionsResult(int requestCode,
String[] permisos,
int[] grantResults){
if(requestCode == SOLICITUD_PERMISO_WRITE_CALL_LOG){
if (grantResults.length == 1 && grantResults[0] ==
PackageManager.PERMISSION_GRANTED) {
borrarLlamada();
} else {
Snackbar.make(vista,"Sin el permiso, no puedo"
+ "realizar la accion", Snackbar.
LENGTH_SHORT).show();
}
}
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}
}

Procedamos a probarlo como se vera en el siguiente video

Como se ve en el video, al presionar el boton nos aparecera un mensaje abajo, si pulsamos Ok nos pedira si queremos confirmar los permisos, en caso negativo nos dara una notificacion que los necesita y en caso afirmativo nos mostrara el mensaje de borrado.
En resumen, hoy hemos visto como trabajan los permisos en Marshmallow, como concediendo accesos en versiones inferiores no funcionan de la misma forma en esta, como se pueden conceder a “mano”, como se puede solicitar al usuario que lo haga, espero les haya sido util sigueme en Twitter, Facebook o Google+ para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios