Entonces listo. Si you tengo gestionados mis permisos, ahora únicamente me falta estar gestionando la consulta del content provider. Y esto lo vamos a implementar en nuestro último método. En nuestro último método, vamos a colocar el TextView que declaramos en nuestro layout que lo definimos. Recuerda que ese TextView estaba dentro de un scroll. Entonces vamos a primeramente obtener el TextView a través de R.id.tvLlamadas. Perfecto. Y antes de cualquier cosa pues vamos a quitarle el texto domi que estamos allí mostrando. Le quitamos el texto domi. Una vez que le hemos quitado el texto domi, vamos a comenzar a gestionar el content provider. Para poder estar consultando el content provider lo primero que yo necesito es una dirección de donde está ubicado ese content provider. Entonces para eso necesitaré un objeto de tipo Uri. Uri con U mayúscula, r minúscula, i minúscula, que es de la paquetería android.net. No te confundas con la otra que está de Java. Y este Uri será un objeto que se llame direcciónUri de la base de datos Llamadas. ¿Y de dónde vamos a obtener esta dirección? Vamos a utilizar una clase auxiliar que se llama CallLog.Calls. Esta clase, si viste por ahí el paquete de esta clase, proviene de android.provider. Ésta you tiene definido en automático todas las direcciones Uri que podemos estar consultando. Entonces como observas por ahí es una variable, una constante como tal. CONTENT_URI está en mayúsculas. Le voy a dar punto y coma. Y si yo quiero puedo presionar command o control en Windows. Y entonces doy clic y ¿qué obtengo? Lo que obtengo es esto, esta es la dirección que vamos a estar consultando, call_log/calls. Entonces en el video anterior vimos la sintaxis de lo que significa esto y entonces listo. Aquí you estoy guardando la dirección de llamadas. Como recuerdas, lo que estaremos consultando será una base de datos, o más bien you definidamente una tabla. Una tabla que se llama Llamadas o por acá lo tenemos que se llama calls, es una tabla que se llama calls. Y entonces esa tabla va a tener campos que nosotros vamos a desear consultar. No necesitamos todos los campos que mostrar, para fines de este ejemplo vamos a definir cuatro campos a mostrar. Yo solamente quiero mostrar el número de la llamada y quiero mostrar la fecha en que se ejecutó la llamada. Quiero mostrar el tipo de llamada, es decir, si fue una llamada perdida, una llamada de entrada, una llamada de salida. Y la duración de la llamada, ¿cuántos segundos duró esa llamada? Entonces necesito estos cuatro campos, no necesito todos los campos de la tabla. Pero aquí sucede algo, yo no sé cómo se llaman estos campos en la tabla. No sé qué nombres tengan estos campos en la tabla. Entonces, para ello, utilizaré también mi clase calls. Que esta clase calls tiene definido toda la estructura de la tabla calls, del proveedor de contenido del content provider. Entonces, voy a crear un arreglo de string que se va a llamar campos. Que esto solamente va a contener todos los campos de la tabla. Entonces, éste va a contener todos los campos de la tabla que voy a estar consultando. Voy a dar un enter para poner el primer campo. Le voy a decir call., necesito el number. Calls, se llama calls en plural. Calls.number. Aquí está el number. Aquí puedes ver todos los campos que puedes estar consultando. NUMBER, voy a dar coma. Ahora voy a colocar de nuevo Calls., necesitamos la fecha, aquí la fecha se llama DATE. Y calls punto, vamos a poner TYPE, que es el tipo de llamada. Entonces algo que debes saber en cuestión del tipo de llamada, es que el tipo de llamada como tal no se va a guardar el texto llamada perdida, o llamada de entrada o llamada de salida. No se va a guardar ese texto. Lo que en esta llamada se está almacenando es un código del tipo de llamada. A lo mejor para llamada perdida le coloca el código uno, para llamada de entrada le coloca el código dos, y así sucesivamente. Entonces nosotros ahorita en un futuro vamos a estar validando qué tipo o qué código de llamada se cachó para cada registo. Y entonces vamos a estar haciendo una validación para eso. Por último, me faltaría la duración de la llamada. you está la duración. Listo. Hasta ahorita sólo tengo los campos que voy a estar consultando de la tabla, que aquí viene toda la dirección you completa de la tabla. Entonces, a continuación lo que necesitamos ahora es definir un objeto de tipo Content.Resolver. Este objeto Content.Resolver me va a ayudar a ejecutar un query sobre esta tabla, esta que tengo aquí, you es la tabla como tal. Entonces yo voy a poner aquí contentResolver, le voy a decir getContentResolver;. Y ahora comenzaré, en el siguiente sería poner contentResolver.query. Si observas esto en Android, vamos a aprender que los resultados se van a estar almacenando. O una interfaz que nos va a ayudar a manejar los resultado de cualquier base de datos. O cuando consultemos una tabla de una base de datos será la interfaz, cursor. Entonces voy a colocar cursor, o podemos poner algo más de un sitio web, registros. Estos van a ser los registros, o más bien esto va ser un objeto que me va a estar ayudando a mostrar los registros o a traer todos los registros. Entonces para poder traer los registros primero necesito ejecutar un query. Entonces el query lo ejecuto con mi objeto que acabo de crear contentResolver. Entonces este query contiene por ahí algunos parámetros. Lo primero que me solicita es la dirección de las llamadas. Voy a quitarlo para poder ver de nuevo los nombres de los parámetros. Entonces aquí está. Lo primero que me está solicitando, aquí observamos, es un Uri que no puede ser nulo. Eso es lo que significa este no null, no puede venir nulo ese Uri. Entonces vamos a colocar aquí la dirección de Uri de llamadas, coma. Lo siguiente que dice que es el campo projection, pues en realidad no es más que los campos que estamos consultando de la tabla de la base de datos. Entonces ahí en projection vamos a colocar nuestro string, nuestro arreglo de string de campos. Lo siguiente es si deseamos que tenga algún filtro en particular. Entonces vamos a decir que no deseamos que tenga algún filtro. Y lo siguiente es el resultado, o sea los valores de los parámetros que en ese caso llevaría el filtro. Ajá, por ejemplo podemos decir tráeme todas las fechas que ocurrieron en el mes de Enero. Entonces el primer valor estaría la condición, y el segundo valor estaría el mes de Enero, o el valor del argumento como tal. Después podemos decir si queremos que nos traiga en algún orden en particular. Entonces yo quiero que me traiga los datos ordenados por fecha. Entonces para ordenarlos por fecha utilizaré mi clase que contiene el nombre del campo, DATE, aquí está. Y además le voy a concatenar, quiero que me las traiga en orden descendente. Recuerda que aquí debe ir un espacio, porque por aquí estoy colocando el nombre del campo. Y entonces ahora lo estoy concatenando únicamente en qué forma quiero que estén ordenados. Entonces ponemos espacio DESC. Ok, entonces vamos a ver. you tenemos contentResolver nuestro query, hemos ejecutado la dirección de llamadas, los campos, los argumentos, no tiene ningún argumento y el ordenamiento de el resultado de ese query. Si observas nos aparece subrayado, ok, vamos a dar un Alt Enter para ver lo que está sucediendo. Lo que nos dice es que necesitamos añadir el permiso de READ_CALL_LOG. Entonces aquí si tú pones esto, pues en realidad lo que va ocurrir es que nos va generar código, donde se esté gestionando. Donde estará haciendo esto que nosotros hicimos en check, checar estatus del permiso. En este momento no nos generó ningún código, porque el código para gestionar el estatus del permiso, you lo tenemos nosotros. Ok, entonces aquí vamos a dejar esto como está subrayado. Si observas, solamente es como un aviso, está subrayado con rojo, pero en nuestro archivo no nos está ocasionando ningún error grave. Entonces lo dejaremos así, porque nosotros you estamos gestionando el permiso. Y ahora, lo que vamos hacer es que una vez obtenidos los registros que estarán aquí, podemos empezar a mostrarlos. Podemos empezar a mostrarlos y para eso vamos a estar iterándolos. Iterándolos con nuestro curso registros, le voy a decir registros.moveToNext. Ok, esto de moveToNext me devuelve un booleano, que quiere decir que si siempre se puede estar moviendo al siguiente registro, entonces puedo estar ejecutando, puedo estar obteniendo el registro. Ok, entonces si siempre se está moviendo al siguiente registro, entonces podré estar extrayendo los datos del registro. Bien, entonces lo que yo quiero extraer, dijimos que queríamos primeramente el número. Vamos a empezar a declarar unas variables locales aquí para obtener el número, le voy a poner por el momento así. Va subir un poco. Y posteriormente vamos a obtener la fecha. Ahora la fecha tampoco es un dato que esté guardando de una forma con formato. Con formato de día mes y año. La fecha que se está abordando, en realidad es un número muy grande, es un número long que corresponde a lo que se conoce como el epoch unix. Por ahí se los recomiendo que lean un poco sobre el epoch unix. Y entonces lo que esto trae en realidad en cuestión de la fecha es un número muy grande. Entonces lo vamos a dejar ahorita así, me va a estar marcando que esto pues en realidad parece un int, este cero es un int, pero ahorita lo vamos a dejar así, ahorita lo corregimos. Después viene el tipo de llamada. Para el tipo de llamada lo que vamos a estar aquí trabajando, como te comenté hace un momento, no es el tipo de cadena de texto llamada perdida. Sino vamos a estar únicamente cachando el código del tipo de llamada. Entonces ese código del tipo de llamada lo estaré guardando en una variable local de tipo entera. Y por último la duración, la duración ésta si la podemos guardar en un string. La podemos estar guardando en un string, vamos a poner aquí duración. Vamos a poner duración así, perfecto. Entonces la duración la vamos a estar guardando en un string, de eso no tenemos problema. Ahora, si nosotros quisiéramos you obtener el número, podemos aprovechar la variable, el objeto que tenemos aquí, de tipo cursor registros, para poder estar obteniendo el valor de esa columna. Entonces el método que necesito se llama getColumnName, que si lo seleccionamos, observamos que nos está solicitando un índice de columna. ¿Un índice de columna a qué se está refieriendo? Bueno que si nuestros datos, imagínate que nuestros datos en una tabla se están guardando de esta forma. Así está estructurada la tabla. Imagínate que así tuviera cuatro campos la tabla. Entonces para este número tendría el índice cero, para la fecha tendría el índice uno, para el tipo dos, y así sucesivamente para el tres. Siempre los índices van comenzando del cero al uno. Y se está refiriendo a la cantidad, se está refiriendo a la posición de los campos, la posición donde está ubicados los campos. Entonces yo como no conozco esta tabla, pues yo no puedo decir o no puedo saber en este momento número, en qué índice de posición está ubicado. Entonces para esto, lo que yo necesitaría hacer aquí dentro es utilizar mi mismo objeto, el objeto que tengo en registros, y decirle .getColumnIndex. Que esto lo que va tener, por aquí lo que va recibir es el nombre de la columna. Entonces para poner el nombre de la columna recuerdas que el nombre del campo, acá los tengo. Tengo campos, tengo número, tengo date, type, duración. Y entonces yo puedo poner aquí, voy a aprovechar campos, los ven aquí en el cero, cero me va traer number y me va traer el nombre del campo. Y entonces, a partir del nombre del campo, yo puedo saber el índice qué número ocupa. A lo mejor antes de número, no sé, tenga un id. Pero yo no sé, yo no lo conozco. Y ahora entonces el id es el cero, número es el uno y así sucesivamente. Entonces a partir de esa instrucción, de getColumnIndex, a partir del nombre del campo, estoy obteniendo el índice que está ocupando en su ordenamiento en la tabla. Una vez que you obtengo el índice, ahora sí you puedo obtener el dato que será el número telefónico de mi log de llamadas. Bien, ahora para la fecha haré exactamente lo mismo. Colocaré registros.getColumnName. Como no me sé el nombre de la columna exactamente, entonces utilizaré el getColumnIndex. Y a partir del nombre del campo es que yo estoy obteniendo you los datos. Perfecto. Entonces fíjate bien lo que está pasando aquí. GetColumnName. GetColumnName. Esto en realidad lo que nos está devolviendo es el nombre de la columna. Yo lo que necesito realmente es solicitar el dato, entonces voy a poner getString. Para el número lo necesito en String, entonces para la fecha le voy a poner getLong. Ahí está. Perfecto. Para el tipo de llamada vamos a poner registros.getInt. Aquí está. Como observas también está solicitando el índice de la columna. Colocamos lo que acabamos de aprender, getColumnIndex. Le pasamos el nombre de la columna que está definido en mi arreglo datos, que en este caso es el lugar número dos. Y ahora para la duración colocaremos registros., lo necesitamos en un string también. Vamos a decir registros.getColumnIndex y colocamos campos y tres, para obtener el nombre de esto. Muy bien. you ahora sí aquí estamos obteniendo los datos. Entonces vamos a acomodar esto un poquito mejor para que se vea mejor, nuestro código se vea más presentable. you tenemos los datos, ahí están. Y entonces nada más nos faltará por acá estar validando el tipo de llamada. Entonces aquí nada más te voy a poner, obtenemos los datos a partir del índice de la columna. Y por acá te voy a poner entonces la validación del tipo de llamada. Bien. Lo que haremos como validación vamos a hacer un switch, donde nuestra variable a inspeccionar será tipo. Entonces en tipo voy a poner case, y diremos que Calls punto. Si ese tipo de llamada coincide con el código de llamada de incoming type, entonces lo que hará será más bien colocar el texto llamada de entrada. Vamos a poner aquí break. Lo mismo será, así entonces, Calls es una llamada perdida. Ponemos aquí break. Y por acá, lo mismo será cuando nuestra llamada sea una llamada de salida, OUTGOING_TYPE. Break. Y si no coincide con ninguno de estos, entonces debe colocarle cualquier otra cosa. Entonces para poder yo estar manejando los textos de llamada de entrada, llamada perdida, estos textos los voy a estar declarando en mi archivo strings. Y los estaré llamando con código Java. Le vamos a poner entrada, así. Llamada de entrada. Vamos a copiar esto un par de veces más. Salida, llamada de salida. Y llamada perdida. Llamada perdida. Muy bien. Y lo voy a poner una vez más, le voy a poner desconocido. Llamada desconocida. A lo mejor no sé qué tipo de llamada sea y depronto necesito validar también eso. Perfecto. Entonces voy a declarar por acá una variable más, String, que le voy a llamar tipo de llamada. Y la voy a dejar vacía por el momento. Y entonces aquí a esa variable de tipo llamada le voy a estar colocando, dependiendo si es incoming type, entonces deberá tener el valor de esta variable string. Para que le asigne el texto llamada de entrada a la variable tipoLlamada. ¿Cómo voy a hacer esto? Voy a colocar getResources y voy a decir .getString, y lo que recibe como parámetro getString es el recurso que definimos en el archivo Strings. Y entonces esto mismo lo vamos a copiar y lo vamos a pegar por acá unas tres veces más. Y entonces para llamada perdida simplemente voy a estar llamando al recurso de perdida. Para llamada de salida vamos a estar llamando al recurso de llamada de salida. Y para cualquier otro caso, un caso desconocido, puedo estar llamando al recurso desconocido. Perfecto you tengo validada, you he obtenido mis datos a partir del índice de la columna, que recibe el nombre de la columna. Y entonces estoy solicitando los datos en diferentes tipos de datos, en strings, en un entero, un string también. Estoy validando el tipo de llamada, y además me faltará también por acá, pues la fecha, estar trabajando con la fecha. La fecha la vamos a manejar, a continuación le vamos a dar un formato también de fecha. Entonces para todo esto, lo voy a tener que estar concatenando en una sola variable que le voy a llamar detalle. Ok, detalle de llamadas. Entonces esto va tener así, y entonces voy a estar definiendo por acá. Lo que yo quiero mostrar es que diga aquí número, dos puntos y entonces me concatene el número, y así sucesivamente. Estas etiquetas, esto que está aquí, vamos a darlo de alta en nuestro archivo Strings. Y entonces vamos a colocar string, y vamos a poner etiqueta_nombre, y esto es lo que vamos a estar poniendo en el número, ok, etiqueta número. Vamos a ponerlo, recuerda que son cuatro campos, etiqueta, la fecha, y le vamos a poner aquí fecha. Etiqueta tipo le vamos a poner aquí tipo. Y etiqueta duración, y le vamos a poner aquí duración. Listo, etiqueta duración. Bien, entonces acá you podemos estar llamando a esos recursos como lo hicimos aquí. Vamos a copiar esto que está aquí, lo ponemos acá, la entrada. Pero en este caso será mi etiqueta de número. Y lo concatenamos con el número. Vamos a ponerle un más y va dar un salto de línea. Y entonces yo quiero que esto, pues que aparezca el número, salto de línea. Por eso coloco antidiagonal n y enseguida me concatene el siguiente registro. Que para eso voy a copiar esto que está aquí y así. Le voy acambiar, en vez de número nececito la fecha, ok. Y entonces para la fecha. Aquí se me está pasando algo, estos punto y coma no tienen que estar ahí, simplemente estamos concatenando valores. Entonces para la fecha, la fecha recuerdas que dijimos que la vamos a formatear. Porque dijimos que si mostramos la fecha, ahorita la fecha viene en un número. En el epoch unix. Entonces la fecha viene en un número, no me interesa mostrarla así, necesito darle formato. Para darle formato a la fecha, utilizaré una clase de Android que se llama date format. Es muy importante que utilicemos la clase de Android. Como observas también está una de Java, y por acá está la de Android, ésta es la que necesitamos. Entonces fíjate muy bien qué paquete es el que se importa cuando selecciones eso. Entonces utilizaremos punto format, y el primer parámetro que recibe es el formato en que quiero mostrar la fecha. El segundo parámetro si observas es un long. Es un long que es la fecha que estamos obteniendo. Entonces en format vamos a decir que queremos la fecha en día, diagonal, mes mes, diagonal año. Ok, y además queremos las horas y la fecha, más bien la hora, en que se ejecutó esa llamada. Para la hora colocamos k y para los minutos colocamos mm. Muy bien, ahora seguimos concatenando fecha, así es como quedaría nuestra fecha. Y colocamos por acá de nuevo, necesitaremos dar un salto de línea y vamos a poner de nuevo por acá get resources. De nuevo voy a copiar esta línea que tengo aquí, así. Ok, entonces como lo que me falta poner aquí, en vez de la fecha sería, como va el orden, el tipo de llamada. Perfecto, el tipo de llamada you lo estoy catchando. Entonces vamos a poner aquí tipo, y lo que voy a estar aquí imprimiendo va ser el tipo de llamada. El tipo de llamada que you contiene desconocido, salida, llamada perdida, llamada de entrada, etc. Muy bien, lo copio de nuevo y ahora voy a colocar. Para una llamada, para mi etiqueta salida, de duración. ¿Cuánto duró mi llamada?, duración. Estos datos pues están dados en segundos, entonces vamos a poner la duración, s punto. Y cerramos con un punto y coma, listo. Así es como se verá en un string. Así es como va estar mi string, ok. Entonces lo estoy haciendo de la mejor forma, trayendo las variables del archivo strings, entonces una vez you tengo lo que voy a estar metiendo en mi edit text. Todavía te acuerdas de mi textView que lo tengo por acá arriba. Bueno, entonces este textView lo vamos a trabajar aquí adentro. Lo que vamos a estar trabajandoes el tvLlamadas, y aquí en vez de estar colocando setText como nos sugiere en este momento. Si yo coloco setText siempre va estar reemplazandose el contenido de ese textView. Entonces todo el tiempo va estar mostrando el último registro o todo el tiempo va estar mostrandome un único registro. Voy autilizar open, para estar concatenando, entonces lo que voy a poner aquí concatenar el detalle. Con esto you podría correr mi aplicación, únicamente me falta un detalle. Los permisos que gestioné aquí, también necesito darles de alta en el archivo manifest. Entonces por último damos de alta nuestros permisos, con la instrucción use permission. Y necesitamos primero el de READ_CALL_LOG, y también, aquí you estaba. También necesitamos el de WRITE_CALL_LOG. Entonces aprovechando que you está ahí, vamos a poner el de WRITE_CALL_LOG. you tenemos nuestros dos permisos. Perfecto, vamos a correr nuestro proyecto. Vamos a correr nuestro proyecto, le voy a decir que lo corra en mi teléfono. Yo you tengo aquí listo mi teléfono, con la aplicación que me estará mostrando todo lo que yo haga en mi teléfono. Y vamos a esperar. Vamos a esperar a que corra, no hay problema. Aquí you acaba de terminar, muy bien. Acaba de terminar Gradle de construir el proyecto y simplemente vamos a esperar a que you lance la aplicación. Vamos a ver aquí en run qué está pasando, y you listo. you está lanzada, al parecer, la aplicación. Aquí viene y se bloqueó el teléfono, ahí está. Dice llamadas, y dice mostrar el log de llamadas. Ok, vamos a darle aquí clic. Lo primero que nos está pidiendo, dice deseas permitir a MiLogLlamadas, es el nombre de nuestra aplicación, hacer y administrar llamadas telefónicas, le decimos que sí, permitir. Y bueno, you nos está mostrando, you está activo el permiso. Y aquí estamos consultando el log de llamadas. Ok, tal vez sería mejor darle un poco más de formato a lo que estamos mostrando. Pero primeramente estamos mostrando el número, después viene la fecha, cuando se dió esta fecha, la hora, el tipo de llamada fue una llamada de salida y la duración. Y entonces aquí en mi scroll estoy mostrando mi log de llamadas.