Bienvenidos sean a este post, hoy vamos a crear un ejemplo para ver como trabajar con ContentProvider y para ello vamos a crear una nueva aplicacion con las siguientes caracteristicas:

  • Application Name: ContentCallLog
  • Company Domain: example.org
  • API SDK Minimum: 14
  • Add an Activity: Empty Activity
  • Activity Name: MainActivity
  • Layout Name: activity_main

Una vez creada nuestra aplicacion debemos modificar el contenido generado en activity_main.xml por el siguiente:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/salida" />

</LinearLayout>

Este layout es bien simple porque como veran solamente tiene un elemento de tipo Textview para mostrar lo que enviemos, nuestro siguiente paso sera modificar el metodo onCreate() de la clase MainActivity de la siguiente forma:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        String [] TIPO_LLAMADA = {"","entrante","saliente",
                "perdida","mensaje de voz", "cancelada",
                "lista bloqueados" };
        TextView salida = (TextView) findViewById(R.id.salida);
        Uri llamadas = Uri.parse("content://call_log/calls");
        Cursor c = getContentResolver().query(llamadas,null,
                null,null,null);
        while (c.moveToNext()){
            salida.append("\n"
                            + DateFormat.format("dd/MM/yy k:mm (",
                    c.getLong(c.getColumnIndex(CallLog.Calls.DATE)))
                    + c.getString(c.getColumnIndex(CallLog.Calls.DURATION)) + ") "
                    + c.getString(c.getColumnIndex(CallLog.Calls.NUMBER)) + ", "
                    + TIPO_LLAMADA[Integer.parseInt(c.getString(
                            c.getColumnIndex(CallLog.Calls.TYPE)))]
            );
        }
        c.close();
    }

Dentro de nuestro metodo onCreate() despues de llamar al layout primero crearemos un array llamado TIPO_LLAMADA donde guardaremos los distintos tipos de llamada que podemos tener en nuestro calllog, despues crearemos el link con nuestro elemento TextView del layout, crearemos un objeto de tipo Uri con el nombre llamadas al cual le asignaremos un parse para las llamadas del calllog, les recomiendo este post para ver el tema URI, luego tendremos un Cursor llamado c para ejecutar un query, lo vimos en este post porque es diferente a rawquery, para finalmente por medio de un while chequearemos todos y cada uno de los elementos contenidos en c, donde usaremos un format de la clase DateFormat para dar una forma correcta al valor contenido en la que coincida con DATE, luego le adicionaremos la duracion de la misma por medio del mismo metodo pero usaremos la constante DURATION, lo mismo para el numero pero para obtener el tipo de llamada usaremos al array TIPO_LLAMADA y por medio de parseInt le enviaremos que obtenemos con la constante TYPE, en todos los casos usamos el metodo getColumnIndex() y el ordinal para obtener la informacion fueron las constantes informadas respectivamente.

Nota: para que funcione correctamente el ejemplo deben usar para DateFormat el del paquete android.text.format.DateFormat

Nuestra siguiente modificacion sera con el manifiesto de Android (AndroidManifest.xml) donde deberemos agregar el permiso para que nuestra aplicacion pueda acceder al log de llamadas (calllog), para ello agregaremos la siguiente linea:

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

Con esta modificacion realizada podemos pasar a probar nuestra aplicacion donde debera verse de la siguiente forma

Obviamente las llamadas pueden variar con respecto a lo contenido en sus emuladores o dispositivo real pero pueden ver como primero sale la fecha y hora con la duracion de la misma, siguiendo el numero y el tipo de llamada si fue entrante, saliente o perdida.

Nuestra siguiente modificacion sera para poder agregar un nuevo elemento en nuestro calllog, para debemos agregar el siguiente bloque entre la linea que muestra nuestro layout y el codigo que agregamos anteriormente:

        ContentValues valores = new ContentValues();
        valores.put(CallLog.Calls.DATE, new Date().getTime());
        valores.put(CallLog.Calls.NUMBER,"3546229918");
        valores.put(CallLog.Calls.DURATION,"45");
        valores.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE);
        Uri nuevoElemento = getContentResolver().insert(
                CallLog.Calls.CONTENT_URI,valores);

El primer objeto se llama valores y es de tipo ContentValues, esta clase nos permitira almacenar pares de datos dentro de nuestro objeto, despues por medio del metodo put() agregaremos primero la constante (que nos servira de identificador) y luego el valor que estara asociada a la referencia, el primer campo sera la fecha, la cual obtendremos por medio del metodo getTime(), luego le pasaremos un numero a NUMBER, despues una duracion a DURATION para finalmente asociar un tipo a TYPE, nuestra siguiente linea se encargara de crear un objeto de tipo Uri llamado nuevoElemento donde por medio de getContentResolver() y el metodo insert() le diremos que es de tipo Uri el contenido y que sera para el calllog, y luego le pasaremos todos los datos contenidos en valores, antes de probar nuevamente nuestra aplicacion debemos agregar la siguiente linea en AndroidManifest.xml:

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

Este nos concedera el permiso de escritura en el calllog, si compilamos y probamos nuestra aplicacion obtendremos una salida semejante a esta

Nota: En los casos anteriores si Android es mayor o igual a la version 6.0 puede generar un error porque debemos habilitar los permisos en la aplicacion instalada, para ello deben ir a la configuracion o ajustes del dispositivo.

Como pueden observar ahora tenemos nua nueva linea como si fuera una llamada nueva pero en realidad es nuestra linea agregada via codigo, siempre y cuando hayamos concedido los permisos pertinentes, para nuestra siguiente modificacion veremos como eliminar un registro de nuestro log de llamadas, para ello utilizaremos el metodo delete() y su sintaxis es la siguiente:

int ContentProvider.delete(Uri uri, String seleccion, String[] arrgSelec);

Donde los campos informados corresponden a:

  • uri, la Uri del ContentProvider a consultar
  • seleccion, clausula SQL correspondiente a WHERE
  • arrgSelec, lista de argumentos utilizados en el parametro de seleccion

Para entenderlo un poco mejor agreguemos la siguiente linea despues del ultimo bloque agregado pero antes del bloque encargado de mostrar los datos:

        getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                "number='3546229918'",null);

En este caso primero le diremos que es un contenido de tipo Uri perteneciente al calllog, luego pasamos el campo (number) el igual y entre comillas simples (‘) el valor a borrar (en este caso el numero que agregamos antes) y luego pasamos null porque no usaremos ningun parametro adicional, si lo compilamos y ejecutamos obtendremos la siguiente salida

En este caso podemos ver como se ha eliminado la linea que agregamos anteriormente dejando los mismos elementos que teniamos al inicio, esto es ideal para cuando necesitamos eliminar informacion que no es necesaria, pasemos a nuestra siguiente modificacion en este caso daremos la posibilidad de modificar un dato de nuestro ContentProvider y para ello se utiliza el metodo update y su sintaxis es la siguiente:

int ContentProvider.update(Uri uri, ContentValues valores, 
             String seleccion, String[] arrgSelec)

En este caso los campos hacen exactamente lo mismo al caso anterior salvo por valores, este campo es donde se envian los nuevos valores para modificar los anteriores, para comprenderlo un poco mejor agreguemos el siguiente bloque entre la linea agregada anteriormente y el bloque para mostrar el contenido:

        ContentValues valores2 = new ContentValues();
        valores2.put(CallLog.Calls.NUMBER,"4444444444");
        getContentResolver().update(CallLog.Calls.CONTENT_URI,valores2,
                "number='1155556666'",null);

Primero crearemos un objeto llamado valores2 de tipo ContentValues que sera utilizado para almacenar un par de datos, primero le pasaremos la constante como referencia y luego un valor (en este caso usamos NUMBER y le pasamos un nuevo numero), luego en el update() al igual que delete() primero pasaremos que es tipo Uri y es para el Calllog, luego pasaremos el nuevo dato almacenado en valores2, despues vendra el condicional y para este caso le diremos el numero que debe reemplazar con la nueva informacion para finalmente dejar como null el ultimo campo porque no pasaremos ninguna informacion adicional, si compilamos y probamos nuestra aplicacion obtendremos la siguiente salida

En este caso podemos observar como se modifico correctamente la informacion solicitado reemplazando los numeros del condicional con la nueva informacion, en estos ultimos dos casos no necesitamos mas modificaciones en AndroidManifest.xml porque en los casos anteriores ya lo configuramos para poder leer y escribir en el calllog, por ultimo vamos a realizar una ultima modificacion en nuestra busqueda, para ello modificaremos la siguiente linea:

Cursor c = getContentResolver().query(llamadas,null,
                null,null,null);

Por el siguiente bloque:

        String[] proyeccion = new String[]{CallLog.Calls.DATE,
                CallLog.Calls.DURATION,
                CallLog.Calls.NUMBER,
                CallLog.Calls.TYPE};
        String[] arrgSelec=new String[]{"1"};
        Cursor c = getContentResolver().query(
                llamadas,
                proyeccion,
                "type = ?",
                arrgSelec,
                "date DESC");

En este caso primero crearemos un array de tipo String llamado proyeccion, donde almacenaremos los cuatro tipos de constantes que usaremos, luego crearemos otro array de tipo String donde almacenaremos un solo valor (en este caso 1) para luego crear nuevamente nuestro Cursor llamado c y volveremos a crear nuestro query pero sin los valores null, en este caso primero pasaremos los valores contenidos en llamadas, luego en proyeccion, para luego pasar un condicional para el campo type (tipo) el signo de interrogacion sera reemplazado por el siguiente campo (arrgSelec) y por ultimo le diremos que lo ordene por la fecha (date) en forma descendente (DESC) si lo compilamos y probamos obtendremos la siguiente salida

En este caso tenemos solamente las llamadas de tipo entrante, no veremos las de tipo salida o perdidas, como podran darse cuenta esto es ideal para cuando necesitamos un tipo de dato especifico, y como lo hable en este post, para los que tengan un conocimiento en SQL les va a resultar un poco mas engorroso pero con el tiempo uno se termina acostumbrando, para obtener otro tipo de llamadas solamente modifiquen el valor correspondiente a arrgSelec tomando como referencia a TIPO_LLAMADA, prueben y vean las distintas salidas que obtendran, antes de finalizar veamos nuestro codigo final de MainActivity:

package org.example.contentcalllog;

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CallLog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.telecom.Call;
import android.text.format.DateFormat;
import android.widget.TextView;

import java.util.Date;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ContentValues valores = new ContentValues();
        valores.put(CallLog.Calls.DATE, new Date().getTime());
        valores.put(CallLog.Calls.NUMBER,"3546229918");
        valores.put(CallLog.Calls.DURATION,"45");
        valores.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE);
        Uri nuevoElemento = getContentResolver().insert(
                CallLog.Calls.CONTENT_URI,valores);
        getContentResolver().delete(CallLog.Calls.CONTENT_URI,
                "number='3546229918'",null);
        ContentValues valores2 = new ContentValues();
        valores2.put(CallLog.Calls.NUMBER,"4444444444");
        getContentResolver().update(CallLog.Calls.CONTENT_URI,valores2,
                "number='1155556666'",null);
        String [] TIPO_LLAMADA = {"","entrante","saliente",
                "perdida","mensaje de voz", "cancelada",
                "lista bloqueados" };
        TextView salida = (TextView) findViewById(R.id.salida);
        Uri llamadas = Uri.parse("content://call_log/calls");
        /* Cursor c = getContentResolver().query(llamadas,null,
                null,null,null); */
        String[] proyeccion = new String[]{CallLog.Calls.DATE,
                CallLog.Calls.DURATION,
                CallLog.Calls.NUMBER,
                CallLog.Calls.TYPE};
        String[] arrgSelec=new String[]{"1"};
        Cursor c = getContentResolver().query(
                llamadas,
                proyeccion,
                "type = ?",
                arrgSelec,
                "date DESC");
        while (c.moveToNext()){
            salida.append("\n"
                    + DateFormat.format("dd/MM/yy k:mm (",
                    c.getLong(c.getColumnIndex(CallLog.Calls.DATE)))
                    + c.getString(c.getColumnIndex(CallLog.Calls.DURATION)) + ") "
                    + c.getString(c.getColumnIndex(CallLog.Calls.NUMBER)) + ", "
                    + TIPO_LLAMADA[Integer.parseInt(c.getString(
                    c.getColumnIndex(CallLog.Calls.TYPE)))]
            );
        }
        c.close();
    }
}

Y tambien veamos el codigo final de AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.example.contentcalllog">

<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>
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.WRITE_CALL_LOG" />

</manifest>

En resumen, hoy hemos visto como se puede consultar nuestro ContentProvider, como se le puede agregar nuevo contenido, como eliminar el mismo y como actualizar, tambien hemos visto como se puede hacer una busqueda mas efectiva y como trabaja query() en el caso de los ContentProvider, espero les haya sido util sigueme en Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.

Anuncios