El proveedor de contactos es un componente de Android potente y flexible que administra el repositorio central de datos de personas en dispositivos Android. El proveedor de contactos es la fuente de datos que ves en la aplicación de contactos del dispositivo, y también puedes acceder a sus datos en tu propia aplicación y transferir datos entre el dispositivo y los servicios en línea. El proveedor considera una gran variedad de fuentes de datos e intenta administrar la mayor cantidad de datos posible para cada persona. Como resultado, su organización es compleja. Por este motivo, la API del proveedor incluye un amplio conjunto de clases de contacto e interfaces que facilitan la recuperación y la modificación de datos.
Esta guía describe lo siguiente:
- Estructura de proveedor básica.
- Cómo recuperar datos del proveedor.
- Cómo modificar datos en el proveedor.
- Cómo escribir un adaptador de sincronización para sincronizar los datos de tu servidor al Proveedor de contactos
Esta guía presupone que conoces los aspectos básicos de los proveedores de contenido de Android. Para obtener más información sobre los proveedores de contenido de Android, lee el Conceptos básicos del proveedor de contenido
Organización del proveedor de contactos
El proveedor de contactos es un componente del proveedor de contenido de Android que Mantiene tres tipos de datos sobre una persona, cada uno de los cuales corresponde a una tabla que ofrece el proveedor, como se ilustra en la figura 1:

Figura 1: Estructura de las tablas del proveedor de contactos.
Generalmente, las tres tablas se denominan con los nombres de sus clases de contrato. Las clases definen constantes para los URI de contenido, los nombres de las columnas y los valores de las columnas que usan las tablas:
-
Tabla
ContactsContract.Contacts
- Las filas representan diferentes personas en función de la adición de filas de contactos sin procesar.
-
Tabla
ContactsContract.RawContacts
- Filas que contienen un resumen de los datos de una persona, específicas para un tipo de usuario y una cuenta de usuario.
-
Tabla
ContactsContract.Data
- Filas que contienen los detalles de un contacto sin procesar, como direcciones de correo electrónico o números de teléfono.
Las demás tablas representadas por las clases de contrato en ContactsContract
son tablas auxiliares que el proveedor de contactos usa para administrar sus operaciones o respaldar funciones específicas en las aplicaciones de telefonía o de contactos del dispositivo.
Contactos sin procesar
Un contacto sin procesar representa los datos de una persona que provienen de un solo tipo de cuenta y cuenta de la fuente de datos. Como el proveedor de contactos admite más de un servicio en línea como origen de datos para una persona, el proveedor de contactos permite usar múltiples contactos sin procesar para la misma persona. El uso de múltiples contactos sin procesar también le permite al usuario combinar los datos de una persona provenientes de más de una cuenta. del mismo tipo de cuenta.
La mayoría de los datos de un contacto sin procesar no se guardan en la tabla ContactsContract.RawContacts
. En su lugar, se almacenan en una o más filas de la tabla ContactsContract.Data
. Cada fila de datos tiene una columna
Data.RAW_CONTACT_ID
contiene el valor RawContacts._ID
de su
fila superior ContactsContract.RawContacts
.
Columnas importantes de contactos sin procesar
Las columnas importantes de la tabla ContactsContract.RawContacts
se indican en la tabla 1. Lee las notas a continuación de la tabla:
Tabla 1: Columnas importantes de contactos sin procesar.
Nombre de la columna | Usar | Notas |
---|---|---|
ACCOUNT_NAME
|
Es el nombre de la cuenta del tipo de cuenta que es el origen de ese contacto sin procesar.
Por ejemplo, el nombre de una cuenta de Google es una de las direcciones de Gmail del dueño del dispositivo. Consulta la siguiente entrada para
ACCOUNT_TYPE para ver más
información.
|
El formato de este nombre es específico para su tipo de cuenta. No es necesariamente una dirección de correo electrónico. |
ACCOUNT_TYPE
|
El tipo de cuenta que es el origen del contacto sin procesar. Por ejemplo, el tipo de una cuenta de Google es com.google . Califica siempre tu tipo de cuenta
con un identificador de dominio para un dominio que usted posea o controle. Esto asegurará que tu tipo de cuenta sea único.
|
Un tipo de cuenta que ofrece datos de contactos generalmente tiene un adaptador de sincronización asociado se sincroniza con el proveedor de contactos. |
DELETED
|
El marcador "eliminado" para un contacto sin procesar. | Esta marca permite que el proveedor de contactos mantenga la fila internamente hasta la sincronización. pueden borrar la fila de sus servidores y, por último, borrarla de sus servidores del repositorio. |
Notas
Las siguientes son notas importantes sobre el
Tabla ContactsContract.RawContacts
:
-
El nombre de un contacto sin procesar no se guarda en su fila en
ContactsContract.RawContacts
. En su lugar, se almacena en la tablaContactsContract.Data
, en una filaContactsContract.CommonDataKinds.StructuredName
. Un contacto sin procesar solo tiene una fila de este tipo en la tablaContactsContract.Data
. -
Precaución: Para usar los datos de tu cuenta en una fila de contacto sin procesar, primero debe estar registrada con el
AccountManager
. Para ello, indica usuarios para agregar el tipo y el nombre de cuenta a la lista de cuentas. Si no el proveedor de contactos eliminará automáticamente la fila de contactos sin procesar.Por ejemplo, si quieres que tu app conserve datos de contactos para el servicio basado en la Web con el dominio
com.example.dataservice
y la cuenta de usuario de tu servicio esbecky.sharp@dataservice.example.com
, el usuario primero debe agregar la cuenta "type" (com.example.dataservice
) y la cuenta "name" (becky.smart@dataservice.example.com
) para que la app pueda agregar filas de contactos sin procesar. Puedes explicar este requisito al usuario en la documentación o puedes solicitarle usuario agregue el tipo y el nombre, o ambos. Tipos y nombres de cuentas se describen con más detalle en la próxima sección.
Orígenes de los datos de contactos sin procesar
Para comprender cómo funcionan los contactos sin procesar, usemos como ejemplo a la usuaria "Emily Dickinson", quien tiene las siguientes tres cuentas de usuario definidas en su dispositivo:
emily.dickinson@gmail.com
emilyd@gmail.com
- Cuenta de Twitter: "belle_of_amherst"
Este usuario habilitó la Sincronización de contactos para las tres cuentas en la Configuración de Cuentas.
Supón que Emily Dickinson abre una ventana del navegador, accede a Gmail como emily.dickinson@gmail.com
, abre Contactos y agrega a “Thomas Higginson”. Más adelante, accede a Gmail como
emilyd@gmail.com
y le envía un correo electrónico a "Thomas Higginson", que
lo agrega como contacto. También sigue a "colonel_tom" (ID de Twitter de Thomas Higginson) en
Twitter.
Como resultado de estas acciones, el proveedor de contactos crea tres contactos sin procesar:
-
Un contacto sin procesar para "Thomas Higginson" asociada con
emily.dickinson@gmail.com
. El tipo de cuenta del usuario es Google. -
Un segundo contacto sin procesar para "Thomas Higginson" asociada con
emilyd@gmail.com
. El tipo de cuenta de usuario también es Google. Hay un segundo contacto sin procesar, aunque el nombre sea idéntico al nombre anterior, ya que la persona se agregó en un tipo de cuenta diferente. - Un tercer contacto sin procesar para "Thomas Higginson" asociado con "belle_of_amherst". El tipo de cuenta de usuario es Twitter.
Datos
Como se indicó anteriormente, los datos para un contacto sin procesar se almacenan en un
La fila ContactsContract.Data
vinculada al contacto sin procesar
Valor _ID
. Esto permite que un solo contacto sin procesar tenga múltiples instancias del mismo
tipo de datos, como direcciones de correo electrónico o números de teléfono. Por ejemplo, si “Thomas Higginson” para emilyd@gmail.com
(la fila de contacto sin procesar de Thomas Higginson asociada con la cuenta de Google emilyd@gmail.com
) tiene una dirección de correo electrónico particular de thigg@gmail.com
y una dirección de correo electrónico de trabajo de thomas.higginson@gmail.com
, el proveedor de contactos almacena las dos filas de direcciones de correo electrónico y las vincula al contacto sin procesar.
Ten en cuenta que en esta tabla se guardan diferentes tipos de datos. La tabla ContactsContract.Data
incluye las filas nombre para mostrar, número de teléfono, correo electrónico, dirección postal, foto y detalles del sitio web. Para ayudar a administrar esto, la
La tabla ContactsContract.Data
tiene algunas columnas con nombres descriptivos,
y otros con nombres genéricos. El contenido de una columna con nombre descriptivo tiene el mismo significado
independientemente del tipo de datos de la fila, mientras que el contenido de una columna con nombre genérico tiene
significados diferentes
según el tipo de datos.
Columnas con nombre descriptivo
Algunos ejemplos de columnas con nombre descriptivo:
-
RAW_CONTACT_ID
-
Es el valor de la columna
_ID
del contacto sin procesar para estos datos. -
MIMETYPE
-
El tipo de datos que se almacena en esta fila, expresados como un tipo de MIME personalizado. El proveedor de contactos
usa los tipos de MIME definidos en las subclases de
ContactsContract.CommonDataKinds
Estos tipos de MIME son de código abierto, y puede usarla cualquier aplicación o adaptador de sincronización que trabaje con el proveedor de contactos. -
IS_PRIMARY
-
Si este tipo de fila de datos puede aparecer más de una vez para un contacto sin procesar, el
Marcas de columna
IS_PRIMARY
la fila de datos que contiene los datos principales para ese tipo. Por ejemplo, el usuario mantiene presionado el número de teléfono de un contacto y selecciona Establecer como predeterminado, luego, la filaContactsContract.Data
que contiene ese número tiene su columnaIS_PRIMARY
configurada en un valor distinto de cero.
Columnas con nombre genérico
Hay 15 columnas genéricas denominadas DATA1
a DATA15
que generalmente están disponibles y cuatro columnas genéricas adicionales, de SYNC1
a SYNC4
, que solo los adaptadores de sincronización pueden usar. Las constantes de las columnas con nombre genérico funcionan siempre, independientemente del tipo de
los datos que contiene la fila.
La columna DATA1
está indexada. El proveedor de contactos siempre usa esta columna para los datos que prevé que serán los más frecuentes en una consulta. Por ejemplo:
de una fila de correo electrónico, esta columna contiene la dirección de correo electrónico real.
Por convención, la columna DATA15
se reserva para almacenar objetos binarios grandes
(BLOB), como miniaturas de fotos.
Nombres de columnas específicos para los diferentes tipos
Para facilitar el trabajo con las columnas para un tipo de fila específico, el proveedor de contactos también proporciona constantes para nombre de columna de tipo específico, que se definen en las subclases de ContactsContract.CommonDataKinds
. Las constantes simplemente dan un
nombre de constante diferente al mismo nombre de columna, lo que te ayuda a acceder a los datos en una fila de un
un tipo determinado.
Por ejemplo, la clase ContactsContract.CommonDataKinds.Email
define las constantes del nombre de columna específico para una fila ContactsContract.Data
que tiene el tipo de MIME Email.CONTENT_ITEM_TYPE
. La clase contiene la constante
ADDRESS
para la dirección de correo electrónico
. El valor real de ADDRESS
es "data1", que coincide con el nombre genérico de la columna.
Precaución: No agregues tus propios datos personalizados a la tabla ContactsContract.Data
usando una fila que tenga uno de los tipos de MIME predefinidos por el proveedor. Si lo haces, podrías perder los datos o provocar que el proveedor
el mal funcionamiento. Por ejemplo, no deberías agregar una fila con el tipo de MIME
Email.CONTENT_ITEM_TYPE
, que contiene un nombre de usuario en lugar de una dirección de correo electrónico en la
la columna DATA1
. Si usas tu propio tipo de MIME personalizado para la fila, no tienes que
para definir tus propios nombres de columna específicos para el tipo y usar las columnas como desees.
La Figura 2 muestra cómo aparecen las columnas descriptivas y las columnas de datos en una
ContactsContract.Data
fila y cómo se superponen los nombres de columnas de tipo específico
los nombres de las columnas genéricas

Figura 2: Nombres de columnas de tipo específico y nombres de columnas genéricos.
Clases de nombres de columnas de tipo específico
La tabla 2 indica las clases de nombres de columnas de tipo específico que se usan con más frecuencia:
Tabla 2: Clases de nombres de columnas de tipo específico
Clase de asignación | Tipo de dato | Notas |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Los datos del nombre para el contacto sin procesar asociado con esta fila de datos. | Un contacto sin procesar tiene una sola fila de este tipo. |
ContactsContract.CommonDataKinds.Photo |
La foto principal del contacto sin procesar asociado con esta fila de datos. | Un contacto sin procesar tiene una sola fila de este tipo. |
ContactsContract.CommonDataKinds.Email |
Una dirección de correo electrónico del contacto sin procesar asociado con esta fila de datos. | Un contacto sin procesar puede tener varias direcciones de correo electrónico. |
ContactsContract.CommonDataKinds.StructuredPostal |
Una dirección postal del contacto sin procesar asociado con esta fila de datos. | Un contacto sin procesar puede tener varias direcciones postales. |
ContactsContract.CommonDataKinds.GroupMembership |
Un identificador que vincula al contacto sin procesar con uno de los grupos en el proveedor de contactos. | Los grupos son una característica opcional para el tipo y el nombre de cuenta. Se describen en más detalles en la sección Grupos de contactos. |
Contactos
El proveedor de contactos combina las filas de contactos sin procesar de todos los tipos y nombres de cuentas. para formar un contacto. Esto facilita la visualización y modificación de todos los datos que un usuario recopiló para una persona. El proveedor de contactos administra la creación de nuevas filas de contacto y la adición de contactos sin procesar a una fila de contacto existente. Ni las aplicaciones ni los adaptadores de sincronización pueden agregar contactos, y algunas columnas en una fila de contacto son de solo lectura.
Nota: Si intentas agregar un contacto al proveedor de contactos con un insert()
, obtendrás una excepción UnsupportedOperationException
. Si intentas actualizar una columna
que aparece como "solo lectura", se ignora la actualización.
El proveedor de contactos crea un contacto nuevo en respuesta a la adición de un nuevo contacto sin procesar que no coincide con los contactos existentes. El proveedor también hace esto si una solicitud los datos del contacto cambian de tal manera que ya no coincide con el contacto con el que estaba adjuntos anteriormente. Si una aplicación o adaptador de sincronización crea un nuevo contacto sin procesar que coincide con un contacto existente, el nuevo contacto sin procesar se agrega al existente contacto.
El proveedor de contactos vincula una fila de contacto para sus filas de contactos sin procesar con la columna _ID
de la fila en la tabla Contacts
. La columna CONTACT_ID
de la tabla de contactos sin procesar
ContactsContract.RawContacts
contiene _ID
valores para
la fila de contactos asociada a cada fila de contactos sin procesar.
La tabla ContactsContract.Contacts
también tiene la columna LOOKUP_KEY
, que es un vínculo “permanente” con la fila de contacto. Debido a que el proveedor de contactos mantiene contactos
automáticamente, puede cambiar el valor _ID
de una fila de contacto
en respuesta a una agregación o sincronización. Incluso si esto ocurre, el URI de contenido CONTENT_LOOKUP_URI
combinado con el LOOKUP_KEY
del contacto aún hará referencia a la fila de contacto de modo que puedas usar LOOKUP_KEY
para mantener vínculos con los contactos "favoritos", etcétera. Esta columna tiene su propio formato, que no está relacionado con el formato de la columna _ID
.
En la figura 3, se ve cómo las tres tablas principales se relacionan entre sí.

Figura 3: Relaciones de la tabla de contactos, contactos sin procesar y detalles.
Precaución: Si publicas tu app en Google Play Store o si La app está en un dispositivo con Android 10 (nivel de API 29) o una versión posterior, ten en cuenta que un conjunto limitado de campos y métodos de datos de contactos queda obsoleto.
De forma periódica, el sistema borra los valores escritos en estos campos de datos:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
También caducan las API usadas para establecer los campos de datos anteriores:
Además, los siguientes campos ya no devuelven contactos frecuentes. Ten en cuenta que algunos de estos campos afectan el posicionamiento de los contactos solo si estos forman parte de un tipo de datos específico.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(afecta solo los tipos de datos Email, Phone, Callable y Contactables) -
ENTERPRISE_CONTENT_FILTER_URI
(afecta solo Correo electrónico, Teléfono, y Se pueden llamar tipos de datos)
Si tus aplicaciones acceden a estos campos o APIs o los actualizan, usa una alternativa . Por ejemplo, puedes cumplir con ciertos casos de uso usando privada proveedores de contenido y otros datos almacenados en tu app o backend y sistemas.
Para verificar que este cambio no afecte la funcionalidad de la app, puedes borrar manualmente estos campos de datos. Para ello, ejecuta el siguiente comando en un dispositivo con Android 4.1 (nivel de API 16) o versiones posteriores:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Datos de los adaptadores de sincronización
Los usuarios ingresan datos de contacto directamente en el dispositivo, pero los datos también fluyen al proveedor de contactos desde servicios web a través de adaptadores de sincronización, que automatizan la transferencia de datos entre el dispositivo y los servicios. Los adaptadores de sincronización se ejecutan en segundo plano
bajo el control del sistema y llaman a los métodos ContentResolver
para administrar los datos.
En Android, el servicio web con el que trabaja un adaptador de sincronización se identifica mediante un tipo de cuenta. Cada adaptador de sincronización trabaja con un tipo de cuenta, pero puede admitir múltiples nombres de cuenta para de ese tipo. Los tipos y los nombres de cuenta se describen brevemente en la sección Fuentes de datos de contactos sin procesar. Las siguientes definiciones ofrecen más detalles, y describir cómo se relacionan el tipo y el nombre de cuenta con los servicios y adaptadores de sincronización.
- Tipo de cuenta
-
Identifica un servicio en el que el usuario tiene datos almacenados. Casi siempre, el usuario tiene que autenticarse con el servicio. Por ejemplo, Contactos de Google es un tipo de cuenta identificada con el código
google.com
. Este valor corresponde al tipo de cuenta que usaAccountManager
. - Nombre de la cuenta
- Identifica una cuenta específica o información de acceso para un tipo de cuenta. Las cuentas de Contactos de Google son las mismas que las cuentas de Google que tienen una dirección de correo electrónico como nombre de cuenta. Otros servicios pueden usar un nombre de usuario de una sola palabra o un identificador numérico.
No es necesario que los tipos de cuenta sean únicos. Un usuario puede configurar varias cuentas de Contactos de Google y descargar sus datos al Proveedor de Contactos esto puede suceder si el usuario tiene un conjunto de contactos personales para un nombre de cuenta personal y otro conjunto para el trabajo. Los nombres de las cuentas son generalmente únicos. Juntos, identifican un flujo de datos específico entre el proveedor de contactos y un servicio externo.
Si quieres transferir los datos de tu servicio al proveedor de contactos, debes escribir tu propio adaptador de sincronización. Esto se describe con más detalle en la sección Adaptadores de sincronización del proveedor de contactos
En la figura 4, se muestra cómo el proveedor de contactos se adapta al flujo de datos de personas. En la casilla denominada "adaptadores de sincronización", cada adaptador está etiquetado con su tipo de cuenta.

Figura 4: Flujo de datos del proveedor de contactos.
Permisos necesarios
Las aplicaciones que quieran acceder al proveedor de contactos deben solicitar los siguientes permisos:
- Acceso de lectura a una o más tablas
-
READ_CONTACTS
, especificado enAndroidManifest.xml
con el<uses-permission>
elemento como<uses-permission android:name="android.permission.READ_CONTACTS">
- Acceso de escritura a una o más tablas
-
WRITE_CONTACTS
, especificado enAndroidManifest.xml
con el<uses-permission>
elemento como<uses-permission android:name="android.permission.WRITE_CONTACTS">
Esos permisos no se extienden a los datos del perfil del usuario. El perfil del usuario y sus los permisos necesarios se analizan en la siguiente sección, El perfil del usuario.
Recuerda que los datos de contacto del usuario son personales y sensibles. A los usuarios les preocupa su privacidad y no quieren que las aplicaciones recopilen datos acerca de ellos ni de sus contactos. Si no queda claro por qué necesitas permiso para acceder a los datos de sus contactos, es posible que te den la aplicación tiene calificaciones bajas o simplemente se negó a instalarla.
Perfil del usuario
La tabla ContactsContract.Contacts
tiene una sola fila que contiene datos de perfil del usuario del dispositivo. Estos datos describen el user
del dispositivo en lugar de
que uno de los contactos del usuario. La fila de contactos del perfil está vinculada a un
de contactos para cada sistema que usa un perfil.
Cada fila de contacto sin procesar del perfil puede tener múltiples filas de datos. Las constantes para acceder al perfil del usuario están disponibles en la clase ContactsContract.Profile
.
El acceso al perfil del usuario requiere permisos especiales. Además de los permisos READ_CONTACTS
y WRITE_CONTACTS
necesarios para leer y escribir, el acceso al perfil del usuario requiere los permisos android.Manifest.permission#READ_PROFILE y android.Manifest.permission#WRITE_PROFILE para obtener acceso de lectura y escritura, respectivamente.
Recuerda que debes tener en cuenta que los perfiles de los usuarios son sensibles. El permiso android.Manifest.permission#READ_PROFILE te permite acceder a los datos de identificación personal del usuario del dispositivo. Asegúrate de explicarle al usuario por qué necesitas permisos de acceso al perfil del usuario en la descripción de tu aplicación.
Para recuperar la fila de contacto que contiene el perfil del usuario, llama a ContentResolver.query()
. Configura el URI de contenido en CONTENT_URI
y no proporciones ningún criterio de selección. También puedes usar este URI de contenido como el URI de base para recuperar datos sin procesar.
contactos o datos para el perfil. Por ejemplo, este fragmento de código recupera datos para el perfil:
Kotlin
// Sets the columns to retrieve for the user profile projection = arrayOf( ContactsContract.Profile._ID, ContactsContract.Profile.DISPLAY_NAME_PRIMARY, ContactsContract.Profile.LOOKUP_KEY, ContactsContract.Profile.PHOTO_THUMBNAIL_URI ) // Retrieves the profile from the Contacts Provider profileCursor = contentResolver.query( ContactsContract.Profile.CONTENT_URI, projection, null, null, null )
Java
// Sets the columns to retrieve for the user profile projection = new String[] { Profile._ID, Profile.DISPLAY_NAME_PRIMARY, Profile.LOOKUP_KEY, Profile.PHOTO_THUMBNAIL_URI }; // Retrieves the profile from the Contacts Provider profileCursor = getContentResolver().query( Profile.CONTENT_URI, projection , null, null, null);
Nota: Si recuperas varias filas de contacto y quieres determinar si alguna de ellas
es el perfil de usuario, prueba el estado
Columna IS_USER_PROFILE
. Esta columna
está establecido en "1" si el contacto es el perfil del usuario.
Metadatos del proveedor de contactos
El proveedor de contactos administra datos que realizan un seguimiento del estado de los datos de los contactos en la
en un repositorio de confianza. Esos metadatos acerca del repositorio se guardan en varios lugares, incluidas las filas de las tablas de contactos sin procesar, datos y contactos, la tabla ContactsContract.Settings
y la tabla ContactsContract.SyncState
. La siguiente tabla muestra el efecto de cada una de esas porciones de metadatos:
Tabla 3: Metadatos del proveedor de contactos
Tabla | Column | Valores | Significado |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
“0”: no se modificó desde la última sincronización. |
Indica los contactos sin procesar que se modificaron en el dispositivo y se deben volver a sincronizar con el
servidor. El proveedor de contactos establece automáticamente el valor cuando se
las aplicaciones actualizan una fila.
Los adaptadores de sincronización que modifican las tablas de datos o de contactos sin procesar siempre deben adjuntar el
string |
"1": Se modificó desde la última sincronización y debe volver a sincronizarse con el servidor. | |||
ContactsContract.RawContacts |
VERSION |
El número de versión de esta fila. | El proveedor de contactos aumenta automáticamente este valor siempre que cambia la fila o los datos relacionados con ella. |
ContactsContract.Data |
DATA_VERSION |
El número de versión de esta fila. | El proveedor de contactos aumenta automáticamente este valor siempre que la fila de datos un cambio. |
ContactsContract.RawContacts |
SOURCE_ID |
Valor de cadena que identifica exclusivamente este contacto sin procesar para la cuenta en en la que se creó. |
Cuando un adaptador de sincronización crea un contacto sin procesar nuevo, esta columna se debe configurar con el ID único del contacto sin procesar en el servidor. Cuando una aplicación para Android crea un contacto sin procesar nuevo, la aplicación debe dejar esta columna vacía. Esto indica que la sincronización
adaptador que cree un nuevo contacto sin procesar en el servidor y obtenga
el valor de SOURCE_ID .
Específicamente, el ID de la fuente debe ser único para cada cuenta. y debe ser estable entre sincronizaciones:
|
ContactsContract.Groups |
GROUP_VISIBLE |
“0” - Los contactos de este grupo no deberían estar visibles en las IU de la aplicación para Android. | Esta columna corresponde a la compatibilidad con los servidores que permiten al usuario ocultar los contactos en a ciertos grupos. |
"1": Los contactos de este grupo pueden ser visibles en las IU de la aplicación. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
“0” - Para esta cuenta y este tipo de cuenta, los contactos que no pertenecen a un grupo se invisible para las IU de la aplicación de Android. |
De forma predeterminada, los contactos son invisibles si ninguno de sus contactos sin procesar pertenece a un grupo (la membresía a un grupo de un contacto sin procesar se indica mediante una o más filas ContactsContract.CommonDataKinds.GroupMembership en la tabla ContactsContract.Data ).
Si configuras este indicador en la fila de la tabla ContactsContract.Settings para un tipo de cuenta y una cuenta, puedes forzar la visibilidad de los contactos sin grupos.
Uno de los usos de esta marca es mostrar los contactos de los servidores que no usan grupos.
|
"1": Para esta cuenta y este tipo de cuenta, los contactos que no pertenecen a un grupo son visibles para las IU de la aplicación. | |||
ContactsContract.SyncState |
(todo) | Usa esta tabla para guardar metadatos de tu adaptador de sincronización. | Con esta tabla, puedes almacenar el estado de la sincronización y otros datos relacionados con la sincronización el dispositivo. |
Acceso al proveedor de contactos
En esta sección, se describen lineamientos para acceder a datos del proveedor de contactos y se enfoca en lo siguiente:
- Consultas a entidades.
- Modificación por lotes.
- Recuperación y modificación con intents.
- Integridad de los datos.
La realización de modificaciones a partir de un adaptador de sincronización también se aborda con más detalle en la sección Adaptadores de sincronización del proveedor de contactos
Consulta de entidades
El hecho de que las tablas del proveedor de contactos estén organizadas en una jerarquía facilita la recuperación de una fila y todas las filas "secundarias" vinculadas a ella. Por ejemplo, para mostrar
toda la información de una persona, quizás quieras recuperar toda
ContactsContract.RawContacts
filas para una sola
ContactsContract.Contacts
fila o todas las
ContactsContract.CommonDataKinds.Email
filas para una sola
ContactsContract.RawContacts
fila. Para facilitar esto, los contactos
El proveedor ofrece construcciones de entidad que actúan como uniones de bases de datos entre
tablas.
Una entidad es como una tabla compuesta de columnas específicas de una tabla principal y su tabla secundaria.
Cuando consultas una entidad, proporcionas una proyección y criterios de búsqueda en función de las columnas disponibles en la entidad. El resultado es una Cursor
que contiene
una fila por cada fila de la tabla secundaria que se recuperó. Por ejemplo, si consultas ContactsContract.Contacts.Entity
para un nombre de contacto y todas las filas ContactsContract.CommonDataKinds.Email
de todos los contactos sin procesar relacionados con ese nombre, obtendrás un Cursor
que contiene una fila para cada fila ContactsContract.CommonDataKinds.Email
.
Las entidades simplifican las consultas. Con una entidad, puedes recuperar todos los datos de contacto de un contacto o contacto sin procesar a la vez, en lugar de tener que consultar primero la tabla principal para obtener un ID y, luego, la tabla secundaria con ese ID. Además, el proveedor de contactos procesa una consulta a una entidad en una sola transacción, lo que garantiza que los datos recuperados se coherentes internamente.
Nota: Por lo general, una entidad no contiene todas las columnas de la tabla principal y la tabla secundaria. Si intentas trabajar con un nombre de columna que no está en la lista de constantes de nombres de columna de la entidad, obtendrás una Exception
.
El siguiente fragmento de código muestra cómo recuperar todas las filas de contacto sin procesar de un contacto. El fragmento de código es parte de una aplicación más grande que tiene dos actividades: "principal" y "detalle". La actividad principal
muestra una lista de filas de contactos; cuando el usuario selecciona una, la actividad envía su ID al detalle
actividad. La actividad de detalle usa ContactsContract.Contacts.Entity
para mostrar todas las filas de datos de todos los contactos sin procesar asociados con el contacto seleccionado.
Este fragmento se tomó del "detalle" actividad:
Kotlin
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY ) // Initializes the loader identified by LOADER_ID. loaderManager.initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this // The context of the activity ) // Creates a new cursor adapter to attach to the list view cursorAdapter = SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0) // flags // Sets the ListView's backing adapter. rawContactList.adapter = cursorAdapter ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ val projection: Array<String> = arrayOf( ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE ) /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ val sortOrder = "${ContactsContract.Contacts.Entity.RAW_CONTACT_ID} ASC" /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return CursorLoader( applicationContext, // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder // Sort by the raw contact ID. ) }
Java
... /* * Appends the entity path to the URI. In the case of the Contacts Provider, the * expected URI is content://com.google.contacts/#/entity (# is the ID value). */ contactUri = Uri.withAppendedPath( contactUri, ContactsContract.Contacts.Entity.CONTENT_DIRECTORY); // Initializes the loader identified by LOADER_ID. getLoaderManager().initLoader( LOADER_ID, // The identifier of the loader to initialize null, // Arguments for the loader (in this case, none) this); // The context of the activity // Creates a new cursor adapter to attach to the list view cursorAdapter = new SimpleCursorAdapter( this, // the context of the activity R.layout.detail_list_item, // the view item containing the detail widgets mCursor, // the backing cursor fromColumns, // the columns in the cursor that provide the data toViews, // the views in the view item that display the data 0); // flags // Sets the ListView's backing adapter. rawContactList.setAdapter(cursorAdapter); ... @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { /* * Sets the columns to retrieve. * RAW_CONTACT_ID is included to identify the raw contact associated with the data row. * DATA1 contains the first column in the data row (usually the most important one). * MIMETYPE indicates the type of data in the data row. */ String[] projection = { ContactsContract.Contacts.Entity.RAW_CONTACT_ID, ContactsContract.Contacts.Entity.DATA1, ContactsContract.Contacts.Entity.MIMETYPE }; /* * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw * contact collated together. */ String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC"; /* * Returns a new CursorLoader. The arguments are similar to * ContentResolver.query(), except for the Context argument, which supplies the location of * the ContentResolver to use. */ return new CursorLoader( getApplicationContext(), // The activity's context contactUri, // The entity content URI for a single contact projection, // The columns to retrieve null, // Retrieve all the raw contacts and their data rows. null, // sortOrder); // Sort by the raw contact ID. }
Cuando termina la carga, LoaderManager
invoca una devolución de llamada a onLoadFinished()
. Uno de los argumentos entrantes para este método es un
Cursor
por los resultados de la consulta En tu propia app, puedes obtener
datos de este Cursor
para mostrarlos o seguir trabajando con ellos.
Modificación por lotes
Siempre que sea posible, debes insertar, actualizar y eliminar datos que se encuentren en el proveedor de contactos en "modo por lotes" creando un ArrayList
de objetos ContentProviderOperation
y llamando a applyBatch()
. Como el proveedor de contactos realiza todas las operaciones de un applyBatch()
en una sola transacción, tus modificaciones nunca saldrán del repositorio de contactos en un estado inconsistente. Una modificación por lotes también facilita la inserción de un contacto sin procesar y sus datos detallados al mismo tiempo.
Nota: Para modificar un único contacto sin procesar, considera enviar un intent a la aplicación de contactos del dispositivo en lugar de manejar la modificación en tu app. Esto se describe con más detalle en la sección Recuperación y modificación con intents.
Puntos de productividad
Una modificación por lotes que contenga una gran cantidad de operaciones puede bloquear otros procesos y provocar que la experiencia general del usuario no sea buena. Para organizar todas las modificaciones que quieras hacer en la menor cantidad de listas independientes posible y, al mismo tiempo, evitar que bloqueen el sistema, debes configurar puntos de productividad para una o más operaciones.
Un punto de productividad es un objeto ContentProviderOperation
que tiene su
Se estableció el valor de isYieldAllowed()
en
true
Cuando el proveedor de contactos encuentra un punto de productividad, pausa su trabajo para
permite que otros procesos se ejecuten y cierra la transacción actual. Cuando el proveedor se reinicia, continúa con la siguiente operación en el ArrayList
y comienza una nueva transacción.
Los puntos de productividad generan más de una transacción por llamada a
applyBatch()
Por este motivo, debes configurar un punto de productividad para un grupo de filas relacionadas de la última operación.
Por ejemplo, debes establecer un punto de productividad para la última operación de un conjunto que agrega un
filas de contactos sin procesar y sus filas de datos asociadas, o la última operación para un conjunto de filas relacionadas
a un solo contacto.
Los puntos de productividad también son una unidad de operación atómica. Todos los accesos entre dos puntos de productividad tienen éxito o fracasan como una sola unidad. Si no se configuran puntos de productividad, las menores una operación atómica es el lote completo de operaciones. Si usas puntos de productividad, evitas que las operaciones degraden el rendimiento del sistema y, al mismo tiempo, te aseguras de que un subconjunto de operaciones sea atómico.
Referencias inversas de modificación
Cuando insertas una nueva fila de contacto sin procesar y sus filas de datos asociadas como un conjunto de
ContentProviderOperation
, debes vincular las filas de datos a
de la fila del contacto sin procesar insertando
_ID
como el valor
Valor RAW_CONTACT_ID
. Sin embargo, este
no está disponible cuando creas la ContentProviderOperation
para la fila de datos, ya que aún no has aplicado
ContentProviderOperation
para la fila del contacto sin procesar. Para solucionar esto, la clase ContentProviderOperation.Builder
tiene el método withValueBackReference()
.
Este método te permite insertar o modificar una columna con los
de una operación anterior.
El withValueBackReference()
tiene dos argumentos:
-
key
- La clave de un par clave-valor. El valor de este argumento debe ser el nombre de una columna en la tabla que estás modificando.
-
previousResult
-
El índice con base 0 de un valor en el array de
ContentProviderResult
objetos deapplyBatch()
Como se aplican las operaciones por lotes, el resultado de cada operación se almacena en un array de resultados intermedio. El valorpreviousResult
es el índice de uno de estos resultados, que se recupera y almacena con elkey
valor. Esto te permite insertar un nuevo registro de contacto sin procesar y recuperar su valor_ID
, para luego hacer una "referencia inversa" al valor cuando agregas una filaContactsContract.Data
.El array de resultados completo se crea cuando llamas por primera vez.
applyBatch()
, con un tamaño igual al deArrayList
deContentProviderOperation
que proporciones Sin embargo, todas los elementos del array de resultados se establecen ennull
y, si intentas para hacer una referencia inversa a un resultado en una operación que aún no se aplicówithValueBackReference()
lanza unaException
.
Los siguientes fragmentos de código muestran cómo insertar un nuevo contacto sin procesar y datos por lotes. Incluyen código que establece un punto de productividad y usa una referencia inversa.
El primer fragmento de código devuelve datos de contacto desde la IU. Para este momento, el usuario ya seleccionó la cuenta para la que se debe agregar el nuevo contacto sin procesar.
Kotlin
// Creates a contact entry from the current UI values, using the currently-selected account. private fun createContactEntry() { /* * Gets values from the UI */ val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition] val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]
Java
// Creates a contact entry from the current UI values, using the currently-selected account. protected void createContactEntry() { /* * Gets values from the UI */ String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); int phoneType = contactPhoneTypes.get( contactPhoneTypeSpinner.getSelectedItemPosition()); int emailType = contactEmailTypes.get( contactEmailTypeSpinner.getSelectedItemPosition());
El siguiente fragmento crea una operación para insertar la fila de contacto sin procesar en la
Tabla ContactsContract.RawContacts
:
Kotlin
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. val ops = arrayListOf<ContentProviderOperation>() /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ var op: ContentProviderOperation.Builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
/* * Prepares the batch operation for inserting a new raw contact and its data. Even if * the Contacts Provider does not have any data for this person, you can't add a Contact, * only a raw contact. The Contacts Provider will then add a Contact automatically. */ // Creates a new array of ContentProviderOperation objects. ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); /* * Creates a new raw contact with its account type (server type) and account name * (user's account). Remember that the display name is not stored in this row, but in a * StructuredName data row. No other data is required. */ ContentProviderOperation.Builder op = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()) .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Builds the operation and adds it to the array of operations ops.add(op.build());
A continuación, el código crea filas de datos para el nombre para mostrar, el teléfono y el correo electrónico.
Cada objeto generador de la operación usa withValueBackReference()
para obtener RAW_CONTACT_ID
. Los puntos de referencia
de vuelta al objeto ContentProviderResult
de la primera operación
que agrega la fila de contacto sin procesar y muestra su nuevo elemento _ID
.
valor. Como resultado, cada fila de datos se vincula automáticamente por su RAW_CONTACT_ID
a la nueva fila ContactsContract.RawContacts
a la que pertenece.
El objeto ContentProviderOperation.Builder
que agrega la fila de correo electrónico está marcado con withYieldAllowed()
, que establece un punto de productividad:
Kotlin
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType) // Builds the operation and adds it to the array of operations ops.add(op.build()) // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType) /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true) // Builds the operation and adds it to the array of operations ops.add(op.build())
Java
// Creates the display name for the new raw contact, as a StructuredName data row. op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * withValueBackReference sets the value of the first argument to the value of * the ContentProviderResult indexed by the second argument. In this particular * call, the raw contact ID column of the StructuredName data row is set to the * value of the result returned by the first operation, which is the one that * actually adds the raw contact row. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to StructuredName .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) // Sets the data row's display name to the name in the UI. .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified phone number and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Phone .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Sets the phone number and type .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType); // Builds the operation and adds it to the array of operations ops.add(op.build()); // Inserts the specified email and type as a Phone data row op = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) /* * Sets the value of the raw contact id column to the new raw contact ID returned * by the first operation in the batch. */ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0) // Sets the data row's MIME type to Email .withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Sets the email address and type .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email) .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType); /* * Demonstrates a yield point. At the end of this insert, the batch operation's thread * will yield priority to other threads. Use after every set of operations that affect a * single contact, to avoid degrading performance. */ op.withYieldAllowed(true); // Builds the operation and adds it to the array of operations ops.add(op.build());
El último fragmento muestra la llamada a
applyBatch()
inserta el nuevo contacto sin procesar y las filas de datos.
Kotlin
// Ask the Contacts Provider to create a new contact Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})") Log.d(TAG, "Creating contact: $name") /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { contentResolver.applyBatch(ContactsContract.AUTHORITY, ops) } catch (e: Exception) { // Display a warning val txt: String = getString(R.string.contactCreationFailure) Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show() // Log exception Log.e(TAG, "Exception encountered while inserting contact: $e") } }
Java
// Ask the Contacts Provider to create a new contact Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" + selectedAccount.getType() + ")"); Log.d(TAG,"Creating contact: " + name); /* * Applies the array of ContentProviderOperation objects in batch. The results are * discarded. */ try { getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops); } catch (Exception e) { // Display a warning Context ctx = getApplicationContext(); CharSequence txt = getString(R.string.contactCreationFailure); int duration = Toast.LENGTH_SHORT; Toast toast = Toast.makeText(ctx, txt, duration); toast.show(); // Log exception Log.e(TAG, "Exception encountered while inserting contact: " + e); } }
Las operaciones por lotes también te permiten implementar el control de simultaneidad optimista, un método para aplicar transacciones de modificación sin tener que bloquear el repositorio subyacente. Para usar este método, debes aplicar la transacción y, luego, comprobar si hay otras modificaciones que podrían haberse realizado al mismo tiempo. Si descubres que se produjo una modificación incoherente, puedes deshacer la transacción y volver a intentarlo.
El control de simultaneidad optimista es útil para un dispositivo móvil, en el que hay un solo usuario a la vez y los accesos simultáneos a un repositorio de datos son muy poco comunes. Como no se produce un bloqueo, no se pierde tiempo en configurar bloqueos ni en esperar que otras transacciones liberen sus bloqueos.
Usar el control de simultaneidad optimista mientras se actualiza
ContactsContract.RawContacts
fila, sigue estos pasos:
-
Recupera la columna
VERSION
del contacto sin procesar junto con los demás datos que obtengas. -
Crea un objeto
ContentProviderOperation.Builder
apto para implementar una restricción usando el métodonewAssertQuery(Uri)
. Para el URI de contenido, usaRawContacts.CONTENT_URI
con el_ID
del contacto sin procesar anexado. -
Para el objeto
ContentProviderOperation.Builder
, llama awithValue()
para comparar elVERSION
al número de versión que acabas de recuperar. -
Para el mismo
ContentProviderOperation.Builder
, llama awithExpectedCount()
para garantizar que esta aserción pruebe solo una fila. -
Llama a
build()
para crear laContentProviderOperation
y, luego, agrega este objeto como el primer objeto en elArrayList
que pasas aapplyBatch()
- Aplica la transacción por lotes.
Si otra operación actualiza la fila de contacto sin procesar entre el momento en que lees la fila y
cuando intentes modificarla, la aserción ContentProviderOperation
fallará y se revertirá todo el lote de operaciones. Luego, puedes volver a intentar el lote o realizar alguna otra acción.
En el siguiente fragmento de código, se muestra cómo crear un ContentProviderOperation
de "afirmación" después de consultar un solo contacto sin procesar con un CursorLoader
:
Kotlin
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)) mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)) } ... // Sets up a Uri for the assert operation val rawContactUri: Uri = ContentUris.withAppendedId( ContactsContract.RawContacts.CONTENT_URI, rawContactID ) // Creates a builder for the assert operation val assertOp: ContentProviderOperation.Builder = ContentProviderOperation.newAssertQuery(rawContactUri).apply { // Adds the assertions to the assert operation: checks the version withValue(SyncColumns.VERSION, mVersion) // and count of rows tested withExpectedCount(1) } // Creates an ArrayList to hold the ContentProviderOperation objects val ops = arrayListOf<ContentProviderOperation>() ops.add(assertOp.build()) // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops) } catch (e: OperationApplicationException) { // Actions you want to take if the assert operation fails go here }
Java
/* * The application uses CursorLoader to query the raw contacts table. The system calls this method * when the load is finished. */ public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Gets the raw contact's _ID and VERSION values rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID)); mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION)); } ... // Sets up a Uri for the assert operation Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID); // Creates a builder for the assert operation ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri); // Adds the assertions to the assert operation: checks the version and count of rows tested assertOp.withValue(SyncColumns.VERSION, mVersion); assertOp.withExpectedCount(1); // Creates an ArrayList to hold the ContentProviderOperation objects ArrayList ops = new ArrayList<ContentProviderOperation>; ops.add(assertOp.build()); // You would add the rest of your batch operations to "ops" here ... // Applies the batch. If the assert fails, an Exception is thrown try { ContentProviderResult[] results = getContentResolver().applyBatch(AUTHORITY, ops); } catch (OperationApplicationException e) { // Actions you want to take if the assert operation fails go here }
Recuperación y modificación con intents
Enviar una intent a la aplicación de contactos del dispositivo te permite acceder al proveedor de contactos de forma indirecta. La intent inicia la IU de la aplicación de contactos del dispositivo, en la que los usuarios realizan tareas asociadas a los contactos. Con este tipo de acceso, los usuarios pueden hacer lo siguiente:
- Seleccionar un contacto de una lista y hacer que se le devuelva a tu aplicación para continuar trabajando allí.
- Editar datos de un contacto existente
- Insertar un nuevo contacto sin procesar para cualquiera de sus cuentas.
- Eliminar un contacto o datos de contacto.
Si el usuario está insertando o actualizando datos, puedes recopilar los datos primero y enviarlos como parte del intent.
Cuando usas intents para acceder al proveedor de contactos a través de la aplicación de contactos del dispositivo, no necesitas escribir una IU ni un código propios para acceder al proveedor. Tampoco es necesario solicitar permiso para leer o escribir al proveedor. La aplicación de contactos del dispositivo puede permiso de lectura delegado para un contacto y, como estás haciendo modificaciones en el por medio de otra aplicación, no necesitas permisos de escritura.
El proceso general de enviar un intent para acceder a un proveedor se describe en detalle en el
en la guía sobre conceptos básicos del proveedor de contenido en la sección "Acceso a datos mediante intents". La acción,
el tipo de MIME y los valores de datos que usas para las tareas disponibles se resumen en la Tabla 4, mientras que
valores extras que puedes usar con
putExtra()
se incluyen en la
documentación de referencia de ContactsContract.Intents.Insert
:
Tabla 4. Intenciones del proveedor de contactos.
Tarea | Acción | Datos | tipo de MIME | Notas |
---|---|---|---|---|
Cómo seleccionar un contacto de una lista | ACTION_PICK |
Uno de los siguientes:
|
Sin uso |
Muestra una lista de contactos sin procesar o una lista de datos de un contacto sin procesar, según el
el tipo de URI de contenido que proporciones.
Llamada
|
Inserta un contacto sin procesar nuevo | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE : Es el tipo de MIME de un conjunto de contactos sin procesar.
|
Muestra la pantalla Agregar contacto de la aplicación de contactos del dispositivo. Se muestran los valores adicionales que agregues a la intent. Si se envía con startActivityForResult() , el URI de contenido del contacto sin procesar recientemente agregado se pasa al método de devolución de llamada onActivityResult() de tu actividad en el argumento Intent , en el campo "datos". Para obtener el valor, llama a getData() .
|
Cómo editar un contacto | ACTION_EDIT |
CONTENT_LOOKUP_URI para
el contacto. La actividad del editor le permitirá al usuario editar cualquier dato asociado con este contacto.
|
Contacts.CONTENT_ITEM_TYPE , un contacto único. |
Muestra la pantalla "Editar contacto" de la aplicación de contactos. Se muestran los valores adicionales que agregues al intent. Cuando el usuario hace clic en Listo para guardar la ediciones, tu actividad vuelve al primer plano. |
Muestra un selector que también pueda agregar datos. | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Este intent siempre muestra la pantalla del selector de la App de Contactos. El usuario puede
elige un contacto para editarlo o agrega uno nuevo. La pantalla de edición o la de Agregar
según la elección del usuario y los datos adicionales que pases en el intent
el código fuente. Si tu app muestra datos de contacto, como un correo electrónico o un número de teléfono, usa este intent para permitirle al usuario agregar datos a un contacto existente.
contacto,
Nota: No necesitas enviar un valor de nombre en los extras de esta intent, ya que el usuario siempre selecciona un nombre existente o agrega uno nuevo. Además, si envías un nombre y el usuario decide realizar una edición, la app de Contactos mostrará el nombre que tú envías y sobrescribirá el valor anterior. Si el usuario no fíjate esto y guarda la edición, se pierde el valor anterior. |
La aplicación de contactos del dispositivo no te permite eliminar un contacto sin procesar ni ninguno de sus datos con un intent. En cambio, para eliminar un contacto sin procesar, usa
ContentResolver.delete()
o ContentProviderOperation.newDelete()
.
En el siguiente fragmento, se muestra cómo construir y enviar un intent que inserte un nuevo archivo sin procesar contacto y datos:
Kotlin
// Gets values from the UI val name = contactNameEditText.text.toString() val phone = contactPhoneEditText.text.toString() val email = contactEmailEditText.text.toString() val company = companyName.text.toString() val jobtitle = jobTitle.text.toString() /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row val contactData = arrayListOf<ContentValues>() /* * Defines the raw contact row */ // Sets up the row as a ContentValues object val rawContactRow = ContentValues().apply { // Adds the account type and name to the row put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type) put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name) } // Adds the row to the array contactData.add(rawContactRow) /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object val phoneRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) // Adds the phone number and its type to the row put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone) } // Adds the row to the array contactData.add(phoneRow) /* * Sets up the email data row */ // Sets up the row as a ContentValues object val emailRow = ContentValues().apply { // Specifies the MIME type for this data row (all data rows must be marked by their type) put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) // Adds the email address and its type to the row put(ContactsContract.CommonDataKinds.Email.ADDRESS, email) } // Adds the row to the array contactData.add(emailRow) // Creates a new intent for sending to the device's contacts application val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply { // Sets the MIME type to the one expected by the insertion activity type = ContactsContract.RawContacts.CONTENT_TYPE // Sets the new contact name putExtra(ContactsContract.Intents.Insert.NAME, name) // Sets the new company and job title putExtra(ContactsContract.Intents.Insert.COMPANY, company) putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle) /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData) } // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent)
Java
// Gets values from the UI String name = contactNameEditText.getText().toString(); String phone = contactPhoneEditText.getText().toString(); String email = contactEmailEditText.getText().toString(); String company = companyName.getText().toString(); String jobtitle = jobTitle.getText().toString(); // Creates a new intent for sending to the device's contacts application Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION); // Sets the MIME type to the one expected by the insertion activity insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE); // Sets the new contact name insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name); // Sets the new company and job title insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company); insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle); /* * Demonstrates adding data rows as an array list associated with the DATA key */ // Defines an array list to contain the ContentValues objects for each row ArrayList<ContentValues> contactData = new ArrayList<ContentValues>(); /* * Defines the raw contact row */ // Sets up the row as a ContentValues object ContentValues rawContactRow = new ContentValues(); // Adds the account type and name to the row rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.getType()); rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName()); // Adds the row to the array contactData.add(rawContactRow); /* * Sets up the phone number data row */ // Sets up the row as a ContentValues object ContentValues phoneRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) phoneRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE ); // Adds the phone number and its type to the row phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone); // Adds the row to the array contactData.add(phoneRow); /* * Sets up the email data row */ // Sets up the row as a ContentValues object ContentValues emailRow = new ContentValues(); // Specifies the MIME type for this data row (all data rows must be marked by their type) emailRow.put( ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE ); // Adds the email address and its type to the row emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email); // Adds the row to the array contactData.add(emailRow); /* * Adds the array to the intent's extras. It must be a parcelable object in order to * travel between processes. The device's contacts app expects its key to be * Intents.Insert.DATA */ insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData); // Send out the intent to start the device's contacts app in its add contact activity. startActivity(insertIntent);
Integridad de los datos
Como el repositorio de contactos contiene datos importantes y sensibles que los usuarios prevén que serán correctos y estarán actualizados, el proveedor de contactos tiene reglas bien definidas para la integridad de los datos. Es tu responsabilidad cumplir con esas reglas cuando modificas datos de contactos. Lo importante las reglas de firewall se enumeran aquí:
-
Agregar siempre una fila de
ContactsContract.CommonDataKinds.StructuredName
por cadaContactsContract.RawContacts
fila que agregues. -
Una fila
ContactsContract.RawContacts
sin una filaContactsContract.CommonDataKinds.StructuredName
en la tablaContactsContract.Data
puede provocar problemas durante la agregación. -
Vincula siempre las filas
ContactsContract.Data
nuevas con su filaContactsContract.RawContacts
superior. -
Una fila
ContactsContract.Data
que no esté vinculada a unaContactsContract.RawContacts
no estará visible en la aplicación de contactos del dispositivo y podría causar problemas con los adaptadores de sincronización. - Modifica datos solo para los contactos sin procesar que te pertenecen.
- Recuerda que, generalmente, el proveedor de contactos administra datos de muchos tipos de cuenta o servicios en línea diferentes. Debes asegurarte de que tu aplicación solo modifique o elimine datos de las filas que te pertenecen, y de que solo inserte datos con un tipo y un nombre de cuenta que tú controles.
-
Usa siempre las constantes definidas en
ContactsContract
y sus subclases de autoridades, URI de contenido, rutas de URI, nombres de columnas, tipos de MIME yTYPE
valores. - Usar estas constantes te ayuda a evitar errores. También se te notificará con el compilador advertencias si alguna de las constantes deja de estar disponible.
Filas de datos personalizados
Al crear y usar tus propios tipos de MIME personalizados, puedes insertar, editar, borrar y recuperar
tus propias filas de datos en la tabla ContactsContract.Data
. Tus filas
se limitan a usar la columna definida en
ContactsContract.DataColumns
, aunque puedes asignar tus propios
los nombres de las columnas de tipo específico
por los nombres de columna predeterminados. En la aplicación de contactos del dispositivo,
se muestran los datos de las filas, pero no se pueden editar ni borrar, y los usuarios no pueden agregar
datos adicionales. Para permitirles a los usuarios modificar tus filas de datos personalizados, debes proporcionar una actividad de editor en tu aplicación.
Para mostrar tus datos personalizados, proporciona un archivo contacts.xml
que contenga un
elemento <ContactsAccountType>
y uno o más de sus
Elementos secundarios <ContactsDataKind>
. Esto se describe con más detalle en la sección <ContactsDataKind> element
.
Para obtener más información acerca de los tipos de MIME personalizados, lee la guía Cómo crear un proveedor de contenido.
Adaptadores de sincronización del proveedor de contactos
El proveedor de contactos está específicamente diseñado para controlar la sincronización de datos de contactos entre un dispositivo y un servicio en línea. Esto les permite a los usuarios descargar datos existentes a un dispositivo nuevo y cargar datos existentes a una cuenta nueva. La sincronización también garantiza que los usuarios tengan a mano los datos más recientes, independientemente de la fuente de las adiciones y los cambios. Otra ventaja de la sincronización es que facilita de contactos disponibles aun cuando el dispositivo no está conectado a la red.
Si bien puedes implementar la sincronización de varias maneras, el sistema Android proporciona un marco de trabajo de sincronización de complementos que automatiza las siguientes tareas:
- Comprobación de la disponibilidad de la red.
- Programación y ejecución de la sincronización en función de las preferencias del usuario.
- Reinicio de sincronizaciones detenidas.
Para usar este marco de trabajo, debes proporcionar un complemento de adaptador de sincronización. Cada adaptador de sincronización es único servicio y proveedor de contenido, pero puede manejar múltiples nombres de cuenta para el mismo servicio. El framework también admite múltiples adaptadores de sincronización para el mismo servicio y proveedor.
Clases y archivos del adaptador de sincronización
Cuando implementas un adaptador de sincronización, lo haces como una subclase de AbstractThreadedSyncAdapter
y lo instalas como parte de una aplicación para Android. El sistema sabe de la existencia del adaptador de sincronización a partir de elementos del manifiesto de tu aplicación y de un archivo XML especial al que hace referencia el manifiesto. El archivo en formato XML define la
el tipo de cuenta de usuario para el servicio en línea y la autoridad del proveedor de contenido, que juntos
identificar de manera única el adaptador. El adaptador de sincronización no se activa hasta que el usuario agrega un
cuenta para el tipo de cuenta del adaptador de sincronización y habilita la sincronización para el contenido
con el que se sincroniza el adaptador de sincronización. En ese momento, el sistema comienza a administrar el adaptador, llamándolo cuando sea necesario para establecer una sincronización entre el proveedor de contenido y el servidor.
Nota: El uso de un tipo de cuenta como parte de la identificación del adaptador de sincronización le permite
para detectar y agrupar adaptadores de sincronización que acceden a diferentes servicios desde la
misma organización. Por ejemplo, los adaptadores de sincronización para los servicios en línea de Google tienen todos el mismo tipo de cuenta com.google
. Cuando los usuarios agregan una Cuenta de Google a sus dispositivos, todas
de los adaptadores de sincronización instalados para los servicios de Google se enumeran juntos; cada adaptador de sincronización
que se muestra se sincroniza con otro proveedor de contenido del dispositivo.
Debido a que la mayoría de los servicios requieren que los usuarios verifiquen su identidad antes de acceder
de datos, el sistema Android ofrece un framework de autenticación similar
que se usa en conjunto con el framework del adaptador de sincronización. El framework de autenticación usa autenticadores de complementos que son subclases de AbstractAccountAuthenticator
. Un autenticador verifica
la identidad del usuario en los siguientes pasos:
- Recopila el nombre del usuario, la contraseña o información similar (la credenciales).
- Envía las credenciales al servicio.
- Examina la respuesta del servicio.
Si el servicio acepta las credenciales, el autenticador puede guardarlas para usarlas más adelante. Debido al framework del autenticador de complementos, el AccountManager
puede proporcionar acceso a cualquier token de autenticación que un autenticador admita y decida exponer, como los tokens de autenticación OAuth2.
Si bien la autenticación no es obligatoria, la mayoría de los servicios de contacto la utilizan. Sin embargo, no es necesario que uses el framework de autenticación de Android para realizar la autenticación.
Implementación de un adaptador de sincronización
Para implementar un adaptador de sincronización del proveedor de contactos, debes comenzar por crear una aplicación para Android que tenga lo siguiente:
-
Un componente
Service
que responda a solicitudes del sistema para vincularse con el adaptador de sincronización -
Cuando el sistema quiere ejecutar una sincronización, llama al servicio
onBind()
para obtener unIBinder
para el adaptador de sincronización. Esto le permite al sistema realizar llamadas entre procesos a los métodos del adaptador. -
El adaptador de sincronización real, implementado como una subclase concreta de
AbstractThreadedSyncAdapter
-
Esta clase tiene la función de descargar datos del servidor, cargar datos del
dispositivo y resolver conflictos. El trabajo principal del adaptador es
esto se hace en el método
onPerformSync()
. Esta clase se debe iniciar como un singleton. -
Una subclase de
Application
. -
Esta clase actúa como una fábrica para el singleton del adaptador de sincronización. Usa el
El método
onCreate()
para crear una instancia del adaptador de sincronización. proporciona un “método get” estático para devolver el singleton al MétodoonBind()
del adaptador de sincronización servicio. -
Opcional: Un componente
Service
que responde a las solicitudes del sistema para la autenticación de usuarios. -
AccountManager
inicia este servicio para comenzar la autenticación el proceso de administración de recursos. El métodoonCreate()
del servicio crea una instancia de autenticador. Cuando el sistema quiere autenticar una cuenta de usuario para el adaptador de sincronización de su aplicación, llama alonBind()
para obtener unIBinder
para el autenticador. Esto le permite al sistema realizar llamadas entre procesos a los métodos del autenticador. -
Opcional: Una subclase concreta de
AbstractAccountAuthenticator
que controla las solicitudes de autenticación. -
Esta clase proporciona métodos que
AccountManager
invoca para autenticar las credenciales del usuario con el servidor. Los detalles del de autenticación de datos pueden variar ampliamente en función de la tecnología del servidor que se esté utilizando. Deberías consulta la documentación del software de tu servidor para obtener más información sobre la autenticación. - Archivos XML que definen el adaptador de sincronización y el autenticador en el sistema.
-
Los componentes del servicio de adaptador de sincronización y autenticador descritos anteriormente son
definidos en
<service>
en el manifiesto de la aplicación. Estos elementos contienen elementos secundarios<meta-data>
que le proporcionan datos específicos al sistema:-
El elemento
<meta-data>
del servicio de adaptador de sincronización apunta al archivo en formato XMLres/xml/syncadapter.xml
. A su vez, este archivo especifica un URI para el servicio web que se sincronizará con el proveedor de contactos y un tipo de cuenta para el servicio web. -
Opcional: El
<meta-data>
del autenticador apunta al archivo en formato XMLres/xml/authenticator.xml
A la vez, este archivo especifica el tipo de cuenta que admite este autenticador, así como recursos de la IU que aparecen durante el proceso de autenticación. El tipo de cuenta especificado en este elemento debe ser el mismo que el tipo de cuenta especificado para el adaptador de sincronización.
-
El elemento
Datos de redes sociales
Los campos Tablas de android.provider.gclid.StreamItemPhotos administrar datos entrantes de redes sociales. Puedes escribir un adaptador de sincronización que agregue datos de transmisión de tu red a estas tablas, o puedes leer datos de transmisión de estas tablas y mostrarla en tu propia aplicación o en ambas. Con estas funciones, tus redes sociales servicios y aplicaciones pueden integrarse en la experiencia de redes sociales de Android.
Texto de redes sociales
Los elementos de la secuencia siempre están asociados con un contacto sin procesar. El
android.provider.gclid.StreamItemsColumns#RAW_CONTACT_ID establece las
Valor de _ID
para el contacto sin procesar. El tipo y nombre de la cuenta sin procesar
contacto también se almacenan en la fila del elemento de flujo.
Guarda los datos de la secuencia en las siguientes columnas:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Obligatorio. El tipo de cuenta del usuario del contacto sin procesar asociado con esta elemento de flujo. Recuerda establecer este valor cuando insertes un elemento de la secuencia.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Obligatorio. El nombre de cuenta del usuario para el contacto sin procesar asociado con este elemento de la secuencia. Recuerda establecer este valor cuando insertes un elemento de la secuencia.
- Columnas identificadoras
-
Obligatorio. Debes insertar las siguientes columnas de identificador cuando
insertar un elemento de la secuencia:
- android.provider.gclid.StreamItemsColumns#CONTACT_ID: El Valor android.provider.BaseColumns#_ID del contacto con el que se transmite esta transmisión está asociado el elemento.
- android.provider.gclid.StreamItemsColumns#CONTACT_LOOKUP_KEY: El valor android.provider.armeabi.ContactsColumns#LOOKUP_KEY del contacto con el que está asociado este elemento de la secuencia.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: Es el valor android.provider.BaseColumns#_ID del contacto sin procesar con el que está asociado este elemento del flujo.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Opcional. Guarda información resumida que puedas mostrar al comienzo de un elemento de flujo.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
El texto del elemento de la secuencia, ya sea el contenido publicado por la fuente del elemento o una descripción de alguna acción que generó el elemento de la secuencia. Esta columna puede contener cualquier formato e imágenes de recursos insertadas que
fromHtml()
puede representar. El proveedor podría truncar o reducir el contenido largo, pero evitará romper las etiquetas. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- Es una cadena de texto que contiene la hora en la que se insertó o actualizó el elemento de la secuencia en forma de milisegundos desde el epoch. Las aplicaciones que insertan o actualizan elementos de una secuencia son responsables de mantener esta columna; el proveedor de contactos no la mantiene automáticamente.
Para mostrar información de identificación para tus elementos de la secuencia, usa android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL y android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE para establecer vínculos con recursos en tu aplicación.
La tabla android.provider.PodSecurityPolic.StreamItems también contiene las columnas. android.provider.fmt.Transmit.StreamItemsColumns#SYNC1 mediante android.provider.subscribe.StreamItemsColumns#SYNC4 para uso exclusivo de adaptadores de sincronización.
Fotos de redes sociales
En la tabla android.provider.ContactsContract.StreamItemPhotos, se guardan fotos asociadas con un elemento de la secuencia. La tabla
Columna android.provider.PodSecurityPolic.StreamItemPhotosColumns#STREAM_ITEM_ID
vínculos a valores de la columna _ID
de
y la tabla android.provider.queda.StreamItems. Las referencias a las fotos se guardan en las siguientes columnas de la tabla:
- Columna android.provider.ContactsContract.StreamItemPhotos#PHOTO (un BLOB).
- Representación binaria de la foto, con el tamaño modificado por el proveedor para su almacenamiento y visualización. Esta columna es compatible con versiones anteriores de Contactos Proveedor que lo usó para almacenar fotos. Sin embargo, en la versión actual, no debes usar esta columna para guardar fotos. En su lugar, usa android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID o android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (ambos se describen en los siguientes puntos) para guardar fotos en un archivo. Esta columna ahora contiene una miniatura de la foto, que está disponible para su lectura.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Es un identificador numérico de una foto de un contacto sin procesar. Agrega este valor a la constante
DisplayPhoto.CONTENT_URI
para obtener un URI de contenido que dirija a un único archivo de foto y, luego, llamaopenAssetFileDescriptor()
para obtener un controlador para el archivo de foto. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
Un URI de contenido que hace referencia directa al archivo de foto correspondiente a la foto representada por esta fila.
Llama a
openAssetFileDescriptor()
con este URI para obtener un controlador para el archivo de foto.
Uso de tablas de redes sociales
Estas tablas funcionan de la misma manera que las otras tablas principales en el proveedor de contactos, excepto por lo siguiente:
- Estas tablas requieren permisos de acceso adicionales. Para leerlos, tu aplicación debe tener el permiso android.Manifest.permission#READ_SOCIAL_STREAM. Para modificarlos, tu aplicación debe tener el permiso android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Para la tabla android.provider.gclid.StreamItems, la cantidad de filas
almacenados para cada contacto sin procesar es limitado. Una vez que se alcanza este límite,
el proveedor de contactos hace espacio para las nuevas filas de elementos de la secuencia al borrar automáticamente
las filas que tienen los
android.provider.queda.StreamItemsColumns#TIMESTAMP. Para obtener la
límite, envía una consulta al URI de contenido
android.provider.fmt.StreamItems#CONTENT_LIMIT_URI. Puedes salir
Todos los argumentos excepto el URI de contenido establecido en
null
La consulta muestra un cursor que contiene una sola fila, con la única columna android.provider.PodSecurityPolic.StreamItems#MAX_ITEMS.
La clase android.provider.gclid.StreamItems.StreamItemPhotos define una subtabla de android.provider. Mira.StreamItemPhotos con la foto. filas para un solo elemento de la secuencia.
Interacciones con redes sociales
Los datos de redes sociales que administra el proveedor de contactos, junto con el la aplicación de contactos del dispositivo, ofrece una forma potente de conectar tu sistema de redes sociales con contactos existentes. Están disponibles las siguientes funciones:
- Sincronizar tu servicio de redes sociales con el proveedor de contactos mediante una sincronización de un usuario, puedes recuperar la actividad reciente de los contactos de un usuario y guardarla en el android.provider.queda.StreamItems y las tablas android.provider.gclid.StreamItemPhotos para usarlas más adelante.
- Además de la sincronización común, puedes activar tu adaptador de sincronización para que recupere datos adicionales cuando el usuario seleccione un contacto para visualizar. Esto permite que tu adaptador de sincronización para recuperar fotos de alta resolución y los elementos más recientes de las novedades del contacto.
- Registrando una notificación en la aplicación de contactos del dispositivo y en la pestaña Contactos Proveedor, puedes recibir un intent cuando se vea un contacto y, en ese momento, actualizar el estado del contacto desde tu servicio Este enfoque puede ser más rápido y usar menos que realizar una sincronización completa con un adaptador de sincronización.
- Los usuarios pueden agregar un contacto a tu servicio de redes sociales mientras miran el contacto en la aplicación de contactos del dispositivo. Puedes habilitar esta opción con la opción “Invitar contacto” atributo, que se habilita con una combinación de una actividad que agrega un contacto existente a tu red y un archivo en formato XML que proporciona la aplicación de contactos del dispositivo y la Proveedor de contactos con los detalles de tu aplicación.
La sincronización común de los elementos de flujo con el proveedor de contactos es igual a otras sincronizaciones. Para obtener más información acerca de la sincronización, consulta la sección Adaptadores de sincronización del proveedor de contactos. El registro de notificaciones y cómo invitar a contactos se abordan en las siguientes dos secciones.
Registro para administrar visualizaciones de redes sociales
Para registrar tu adaptador de sincronización para que reciba notificaciones cuando el usuario vea un contacto que esté administrada por tu adaptador de sincronización:
-
Crea un archivo llamado
contacts.xml
en el directoriores/xml/
de tu proyecto. Si ya tienes este archivo, puedes omitir este paso. -
En este archivo, agrega el elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
Si el elemento ya existe, puedes omitir este paso. -
Para registrar un servicio que reciba una notificación cuando el usuario abra la página de detalles de un contacto en la aplicación de contactos del dispositivo, agrega el atributo
viewContactNotifyService="serviceclass"
al elemento, en el queserviceclass
es el nombre de clase completo del servicio que debe recibir la intent de la aplicación de contactos del dispositivo. Para el notificador servicio, usa una clase que extiendaIntentService
para permitir que el servicio para recibir intents. Los datos de la intent entrante contienen el URI de contenido del contacto sin procesar en el que el usuario hizo clic. Desde el servicio de notificación, puedes establecer un vínculo con el adaptador de sincronización y, luego, llamarlo para actualizar los datos del contacto sin procesar.
Para registrar una actividad a la que se llamará cuando el usuario haga clic en un elemento o una foto de la secuencia (o en ambos):
-
Crea un archivo llamado
contacts.xml
en el directoriores/xml/
de tu proyecto. Si ya tienes este archivo, puedes omitir este paso. -
En este archivo, agrega el elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Si el elemento ya existe, puedes omitir este paso. -
A fin de registrar una de tus actividades para que el usuario haga clic en un elemento de la secuencia en la
la aplicación de contactos del dispositivo, agrega el atributo
viewStreamItemActivity="activityclass"
al elemento, dondeactivityclass
es el nombre de clase completamente calificado de la actividad. que debe recibir el intent de la aplicación de contactos del dispositivo. -
Para registrar una de tus actividades para que administre al usuario que hace clic en una foto de una secuencia en la aplicación de contactos del dispositivo, agrega el atributo
viewStreamItemPhotoActivity="activityclass"
al elemento, en el queactivityclass
es el nombre de clase completo de la actividad que debe recibir la intent de la aplicación de contactos del dispositivo.
El elemento <ContactsAccountType>
se describe con más detalle en la sección Elemento <ContactsAccountType>.
La intent entrante contiene el URI de contenido del elemento o la foto en los que el usuario hizo clic. Para tener actividades independientes para los elementos de texto y las fotos, usa ambos atributos en el mismo archivo.
Interacción con tu servicio de redes sociales
No es necesario que los usuarios salgan de la aplicación de contactos del dispositivo para invitar a un contacto a tu sitio de redes sociales. En su lugar, puedes hacer que la app de contactos del dispositivo envíe una intent para invitar al contacto a una de tus actividades. Sigue estos pasos para configurar la función:
-
Crea un archivo llamado
contacts.xml
en el directoriores/xml/
de tu proyecto. Si ya tienes este archivo, puedes omitir este paso. -
En este archivo, agrega el elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
Si el elemento ya existe, puedes omitir este paso. -
Agrega los siguientes atributos:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
es el nombre de clase completamente calificado de la actividad que debe recibir el intent. Elinvite_action_label
value es una cadena de texto que se muestra en el menú Agregar conexión en la la aplicación de contactos del dispositivo.
Nota: ContactsSource
es un nombre de etiqueta obsoleto para
ContactsAccountType
Referencia contacts.xml
El archivo contacts.xml
contiene elementos XML que controlan la interacción de tu adaptador de sincronización y tu aplicación con la aplicación de contactos y el proveedor de contactos. Estos
de la infraestructura se describen en las siguientes secciones.
<Tipo de cuentadecontactos> elemento
El elemento <ContactsAccountType>
controla la interacción de tus
de la aplicación de contactos
con la aplicación de contactos. Tiene la siguiente sintaxis:
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android" inviteContactActivity="activity_name" inviteContactActionLabel="invite_command_text" viewContactNotifyService="view_notify_service" viewGroupActivity="group_view_activity" viewGroupActionLabel="group_action_text" viewStreamItemActivity="viewstream_activity_name" viewStreamItemPhotoActivity="viewphotostream_activity_name">
se incluye en:
res/xml/contacts.xml
Puede contener lo siguiente:
<ContactsDataKind>
Description:
Declara componentes de Android y etiquetas de la IU que permiten a los usuarios invitar a uno de sus contactos a una red social, notificar a los usuarios cuando se actualiza uno de sus flujos de redes sociales, y y así sucesivamente.
Ten en cuenta que el prefijo de atributo android:
no es necesario para los atributos.
de <ContactsAccountType>
.
Atributos:
inviteContactActivity
- El nombre de clase completamente calificado de la actividad de tu aplicación que deseas se activa cuando el usuario selecciona Agregar conexión en la contactos de la empresa.
inviteContactActionLabel
-
Es una cadena de texto que se muestra para la actividad especificada en
inviteContactActivity
, en el menú Agregar conexión. Por ejemplo, puedes usar la cadena "Sigue mi red". Puedes usar un recurso de cadenas identificador de esta etiqueta. viewContactNotifyService
- El nombre de clase completamente calificado de un servicio en tu aplicación que debe recibir y las notificaciones cuando el usuario visualiza un contacto. Esta notificación, enviada por la aplicación de contactos del dispositivo, le permite a tu aplicación posponer operaciones que involucran una gran cantidad de datos hasta que sean necesarias. Por ejemplo, tu aplicación puede responder a esta notificación leyendo y mostrando la foto de alta resolución del contacto y la elementos de las redes sociales. Esta función se describe con más detalle en la sección Interacciones con redes sociales.
viewGroupActivity
- El nombre de clase completamente calificado de una actividad de tu aplicación que se puede mostrar la información del grupo. Cuando el usuario hace clic en la etiqueta del grupo en los contactos del dispositivo app, se muestra la IU de esta actividad.
viewGroupActionLabel
-
La etiqueta que muestra la aplicación de contactos para un control de la IU que le permite
al usuario ver los grupos en tu aplicación.
Se puede usar un identificador de recursos de cadena para este atributo.
viewStreamItemActivity
- Es el nombre de clase completo de una actividad de tu aplicación que la aplicación de contactos se inicia cuando el usuario hace clic en un elemento de la secuencia para un contacto sin procesar.
viewStreamItemPhotoActivity
- Es el nombre de clase completo de una actividad de tu aplicación que la aplicación de contactos del dispositivo inicia cuando el usuario hace clic en una foto del elemento del flujo para un contacto sin procesar.
Elemento <ContactsDataKind>
El elemento <ContactsDataKind>
controla cómo se muestran los componentes
filas de datos personalizados en la IU de la aplicación de contactos. Tiene la siguiente sintaxis:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
contenido en:
<ContactsAccountType>
Description:
Usa este elemento para que la aplicación de contactos muestre el contenido de una fila de datos personalizados como parte de los detalles de un contacto sin procesar. Cada elemento secundario <ContactsDataKind>
de <ContactsAccountType>
representa un tipo de fila de datos personalizados que tu adaptador de sincronización agrega a la tabla ContactsContract.Data
. Agregar uno
<ContactsDataKind>
para cada tipo de MIME personalizado que uses. No necesitas agregar el elemento si tienes una fila de datos personalizados para la que no quieres mostrar datos.
Atributos:
android:mimeType
-
Es el tipo de MIME personalizado que definiste para uno de los tipos de fila de datos personalizados en la
tabla
ContactsContract.Data
. Por ejemplo, el valorvnd.android.cursor.item/vnd.example.locationstatus
podría ser un tipo personalizado Es el tipo de MIME de una fila de datos que registra la última ubicación conocida de un contacto. android:icon
- Un dispositivo Android recurso de elementos de diseño que la aplicación de contactos muestra junto a tus datos. Úsalo para indicar al usuario de que los datos provienen de tu servicio.
android:summaryColumn
- Es el nombre de la columna del primer valor de los dos recuperados de la fila de datos. El se muestra como la primera línea de la entrada de esta fila de datos. Se prevé que la primera línea se use como resumen de los datos, pero es opcional. Consulta también android:detailColumn.
android:detailColumn
-
El nombre de columna para los segundos dos valores recuperados de la fila de datos. El valor es
se muestra como la segunda línea de la entrada de esta fila de datos. Consulta también
android:summaryColumn
Otras funciones del proveedor de contactos
Además de las funciones principales descritas en las secciones anteriores, el proveedor de contactos ofrece estas funciones útiles para trabajar con datos de contactos:
- Grupos de contactos
- Funciones de fotografía
Grupos de contactos
Opcionalmente, el proveedor de contactos puede etiquetar conjuntos de contactos relacionados con
group. Si el servidor asociado con una cuenta de usuario quiere mantener los grupos, el adaptador de sincronización del tipo de cuenta de la cuenta debe transferir datos de grupo entre el proveedor de contactos y el servidor. Cuando los usuarios agregan un nuevo contacto al servidor y lo colocan en un grupo nuevo después, el adaptador de sincronización debe agregar el nuevo grupo a la tabla ContactsContract.Groups
. Es la forma en que se agrupa
al que pertenece un contacto se almacenan en la tabla ContactsContract.Data
, mediante
el tipo de MIME ContactsContract.CommonDataKinds.GroupMembership
.
Si estás diseñando un adaptador de sincronización que agregará datos de contactos sin procesar del servidor al proveedor de contactos y no estás usando grupos, debes indicarle al proveedor que permita que tus datos estén visibles. En el código que se ejecuta cuando un usuario agrega una cuenta al dispositivo, actualiza la fila ContactsContract.Settings
que el proveedor de contactos agrega para la cuenta. En esta fila, establece el valor del
Settings.UNGROUPED_VISIBLE
a 1. Cuando haces esto, el proveedor de contactos siempre permitirá que los datos de tus contactos estén visibles, aunque no uses grupos.
Fotos de contactos
La tabla ContactsContract.Data
almacena fotos como filas con tipo de MIME
Photo.CONTENT_ITEM_TYPE
La columna CONTACT_ID
de la fila está vinculada a la columna _ID
del contacto sin procesar al que pertenece.
La clase ContactsContract.Contacts.Photo
define una subtabla de ContactsContract.Contacts
que contiene información de fotografía de la foto principal de un contacto, que es la foto principal del contacto sin procesar principal del contacto. De manera similar, la clase ContactsContract.RawContacts.DisplayPhoto
define una subtabla de ContactsContract.RawContacts
que contiene información de fotografía para la foto principal de un contacto sin procesar.
La documentación de referencia de ContactsContract.Contacts.Photo
y
ContactsContract.RawContacts.DisplayPhoto
contienen ejemplos de
recuperando información de la foto. No hay una clase que resulte más conveniente para recuperar la miniatura principal de un contacto sin procesar, pero puedes enviar una consulta a la tabla ContactsContract.Data
seleccionando en el _ID
, el Photo.CONTENT_ITEM_TYPE
y la columna IS_PRIMARY
del contacto sin procesar para encontrar la fila de foto principal del contacto sin procesar.
Los datos de redes sociales de una persona también pueden incluir fotos. Estos se almacenan la tabla android.provider. determinadas.StreamItemPhotos, que se describe en más detalle. detalles en la sección Fotos de redes sociales.