O Provedor de Contatos é um componente poderoso e flexível do Android que gerencia o repositório central de dados sobre pessoas do dispositivo. O Provedor de contatos é a fonte de dados que você vê no aplicativo de contatos do dispositivo, e também pode acessar os dados no seu próprio e transferem dados entre o dispositivo e os serviços on-line. O provedor fornece uma grande variedade de fontes de dados e tenta gerenciar o máximo de dados possíveis para cada pessoa, uma vez que organizá-los é algo complexo. Por isso, a API do provedor inclui um conjunto extensivo de classes e interfaces de contrato que facilitam a recuperação e a modificação de dados.
Este guia descreve o seguinte:
- A estrutura básica do provedor.
- Como recuperar dados por um provedor.
- Como modificar dados no provedor.
- Como criar um adaptador de sincronização para sincronizar dados do servidor com o Provedor de contatos.
Este guia considera que o leitor conhece os fundamentos sobre provedores de conteúdo do Android. Para saber mais sobre provedores de conteúdo do Android, leia Guia de noções básicas do provedor de conteúdo (em inglês).
Organização do Provedor de contatos
O Provedor de contatos é um componente do provedor de conteúdo do Android. Ele mantém três tipos dados sobre uma pessoa, sendo que cada uma corresponde a uma tabela oferecida pelo fornecedor, conforme ilustrado na figura 1:

Figura 1. Estrutura da tabela do Provedor de Contatos.
As três tabelas são comumente identificadas pelo nome de suas classes de contrato. As classes definem constantes para URIs de conteúdo, nomes e valores de colunas usados pelas tabelas:
-
Tabela
ContactsContract.Contacts
- Linhas que representam pessoas diferentes com base em agregações de linhas de contato brutos.
-
Tabela
ContactsContract.RawContacts
- Linhas com um resumo dos dados de uma pessoa, específicos a um tipo e uma conta de usuário.
-
ContactsContract.Data
tabela - Linhas contendo os detalhes do contato bruto, como endereços de e-mail ou números de telefone.
As outras tabelas representadas por classes de contrato em ContactsContract
são tabelas auxiliares que o Provedor de contatos usa para gerenciar suas operações ou dar suporte
funções específicas nos contatos do dispositivo ou aplicativos de telefonia.
Contatos brutos
Os contatos brutos representam os dados de uma pessoa provenientes de um tipo único de conta e um nome de conta. Como o Provedor de contatos permite mais de um serviço on-line como fonte de dados de uma pessoa, o Provedor de contatos permite diversos contatos brutos para a mesma pessoa. Diversos contatos brutos também permitem que um usuário combine os dados de uma pessoa de mais de uma conta com o mesmo tipo de conta.
A maioria dos dados de um contato bruto não é armazenada
Tabela ContactsContract.RawContacts
. Em vez disso, eles são armazenados
de linhas na tabela ContactsContract.Data
. Cada linha de dados tem uma coluna
Data.RAW_CONTACT_ID
que
contém o valor RawContacts._ID
do
linha ContactsContract.RawContacts
mãe.
Colunas importantes de contatos brutos
As colunas importantes na tabela ContactsContract.RawContacts
são
listados na tabela 1. Leia as observações que se seguem após a tabela:
Tabela 1. Importantes colunas de contatos brutos.
Nome da coluna | Uso | Observações |
---|---|---|
ACCOUNT_NAME
|
É o nome da conta para o tipo de conta que é a fonte desse contato bruto.
Por exemplo, o nome da conta de uma Conta do Google é um dos endereços do Gmail do proprietário
do dispositivo. Confira a próxima entrada de
ACCOUNT_TYPE para mais
informações.
|
O formato desse nome é específico deste tipo de conta. Não se trata necessariamente de um endereço de e-mail. |
ACCOUNT_TYPE
|
O tipo de conta que é a origem desse contato bruto. Por exemplo, a conta
o tipo de uma Conta do Google é com.google . Sempre qualifique seu tipo de conta
com um identificador para um domínio que você possui ou controla. Isso garante que seu
tipo de conta seja exclusivo.
|
Os tipos de conta que fornecem dados de contatos normalmente têm um adaptador de sincronização associado que se sincroniza com o Provedor de contatos. |
DELETED
|
A flag "excluído" de um contato bruto. | Esse sinalizador permite que o Provedor de Contatos mantenha a linha internamente até que os adaptadores de sincronização possam excluí-la dos servidores e, em seguida, excluí-la do repositório. |
Observações
Confira a seguir observações importantes sobre a tabela ContactsContract.RawContacts
:
-
O nome de um contato bruto não é armazenado na linha em
ContactsContract.RawContacts
. Em vez disso, ele é armazenado na tabelaContactsContract.Data
, em uma linhaContactsContract.CommonDataKinds.StructuredName
. Um contato bruto tem apenas uma linha desse tipo na tabelaContactsContract.Data
. -
Atenção:para usar os dados da sua própria conta em uma linha de contato bruto, é necessário
precisam ser registrados com
AccountManager
. Para isso, faça um comando adicionem o tipo e o nome da conta à lista de contas. Se você não fizer isso, o Provedor de contatos excluirá automaticamente a linha do contato bruto.Por exemplo, se você quiser que o app mantenha dados de contato do seu serviço baseado na Web com o domínio
com.example.dataservice
e a conta do usuário do serviço forbecky.sharp@dataservice.example.com
, o usuário precisará primeiro adicionar o "tipo" (com.example.dataservice
) e o "nome" (becky.smart@dataservice.example.com
) da conta antes que o app possa adicionar linhas de contato bruto. Você pode explicar esse requisito ao usuário em documentações ou pode exigir que o usuário adicione o tipo, o nome ou ambos. Os tipos e nomes de conta são descritos com mais detalhes na próxima seção.
Fontes de dados de contatos brutos
Para entender como os contatos brutos funcionam, considere a usuária "Emily Dickinson" que tem o seguinte três contas de usuário definidas no dispositivo:
emily.dickinson@gmail.com
emilyd@gmail.com
- Conta do Twitter "belle_of_amherst"
Este usuário ativou a opção Sincronizar contatos nas três contas Contas.
Suponha que Emily Dickinson abra uma janela do navegador, acesse o Gmail como
emily.dickinson@gmail.com
, abra
Contatos e adicione "Thomas Higginson". Depois, ela faz login no Gmail
emilyd@gmail.com
e envia um e-mail para "Thomas Higginson", que automaticamente
o adiciona como um contato. Ela também segue "colonel_tom" (ID de Thomas Higginson no Twitter) em
no Twitter.
O Provedor de contatos cria três contatos brutos como resultado desse trabalho:
-
Um contato bruto de "Thomas Higginson" associado a
emily.dickinson@gmail.com
. O tipo de conta do usuário é Google. -
Um segundo contato bruto de "Thomas Higginson" associado a
emilyd@gmail.com
. O tipo da conta de usuário também é do Google. Há um segundo contato bruto, embora o nome seja idêntico a um nome anterior porque a pessoa foi adicionada a uma conta de usuário diferente. - Um terceiro contato bruto para "Thomas Higginson" associadas a "belle_of_amherst". O usuário o tipo de conta é Twitter.
Dados
Como observado anteriormente, os dados de um contato bruto são armazenados em um
A linha ContactsContract.Data
que está vinculada ao contato bruto
Valor de _ID
. Isso permite que um único contato bruto tenha diversas instâncias do mesmo
tipo de dados, como endereços de e-mail ou números de telefone. Por exemplo, se
"Thomas Higginson" para emilyd@gmail.com
(a linha de contato bruto de Thomas Higginson)
associadas à Conta do Google emilyd@gmail.com
) tem um endereço de e-mail residencial de
thigg@gmail.com
e um endereço de e-mail comercial de
thomas.higginson@gmail.com
, o Provedor de contatos armazena os dois endereços de e-mail
linhas e as vincula ao contato bruto.
Diferentes tipos de dados são armazenados nessa única tabela. As linhas de nome de exibição,
número de telefone, e-mail, endereço postal, foto e detalhes do site são encontradas na
tabela ContactsContract.Data
. Para ajudar a gerenciar isso, o
A tabela ContactsContract.Data
tem algumas colunas com nomes descritivos,
e outros com nomes genéricos. O conteúdo de uma coluna de nome descritivo tem o mesmo significado
independente do tipo de dado da linha, enquanto o conteúdo de uma coluna de nome genérico tem
significados diferentes dependendo do tipo de dado.
Nomes descritivos de colunas
Veja alguns exemplos de nomes descritivos de colunas:
-
RAW_CONTACT_ID
-
O valor da coluna
_ID
do contato bruto para esses dados. -
MIMETYPE
-
O tipo de dados armazenados nessa linha, expresso como um tipo MIME personalizado. O Provedor de Contatos
usa os tipos MIME definidos nas subclasses de
ContactsContract.CommonDataKinds
. Esses tipos MIME são de código aberto, e pode ser usado por qualquer aplicativo ou adaptador de sincronização que funcione com o Provedor de Contatos. -
IS_PRIMARY
-
Se esse tipo de linha de dados puder ocorrer mais de uma vez em um contato bruto, o
Sinalizações de coluna
IS_PRIMARY
a linha de dados que contém os dados primários para o tipo. Por exemplo, se o usuário tocar em um número de telefone de um contato e o manter pressionado e selecionar Definir padrão; e a linhaContactsContract.Data
que contém o número tem a colunaIS_PRIMARY
definida como um valor diferente de zero.
Nomes de colunas genéricas
Há 15 colunas genéricas de nome DATA1
a
DATA15
que estão disponíveis de forma geral e quatro colunas genéricas
adicionais SYNC1
a SYNC4
que devem ser usadas somente por adaptadores
de sincronização. As constantes de nome de coluna genérica sempre funcionam, independentemente do tipo de
dados que a linha contém.
A coluna DATA1
está indexada. O Provedor de contatos sempre usa essa coluna para
os dados que o provedor espera que sejam os alvos mais frequentes de uma consulta. Por exemplo:
em uma linha de e-mail, essa coluna contém o endereço de e-mail real.
Por convenção, a coluna DATA15
é reservada para armazenar dados de BLOBs
(Binary Large Object), como miniaturas de fotos.
Nomes de coluna de tipo específico
Para facilitar o trabalho com as colunas para um tipo específico de linha, o Provedor de contatos
também fornece constantes de nome de colunas de tipo específico, definidas em subclasses de
ContactsContract.CommonDataKinds
. As constantes simplesmente dão uma
nome de constante diferente para o mesmo nome de coluna, o que ajuda você a acessar dados em uma linha de uma
de um tipo específico.
Por exemplo, a classe ContactsContract.CommonDataKinds.Email
define
constantes de nome de coluna de tipo específico para uma linha ContactsContract.Data
que tem o tipo MIME
Email.CONTENT_ITEM_TYPE
. A classe contém a constante
ADDRESS
para o endereço de e-mail
. O valor real do
ADDRESS
é "data1", que é
é igual ao nome genérico da coluna.
Atenção: não adicione seus próprios dados personalizados à
tabela ContactsContract.Data
usando uma linha que tenha um dos
tipos de MIME predefinidos do provedor. Caso contrário, há o risco de perda de dados ou mau funcionamento do provedor. Por exemplo, não adicione uma linha com o tipo MIME
Email.CONTENT_ITEM_TYPE
que contém um nome de usuário em vez de um endereço de e-mail no
coluna DATA1
. Se usar seu próprio tipo MIME personalizado para a linha, você poderá
para definir seus próprios nomes de coluna de tipo específico e usar as colunas como quiser.
A Figura 2 mostra como colunas descritivas e colunas de dados aparecem em um
ContactsContract.Data
, e como o nome de colunas de tipos específicos é "sobreposição"
os nomes genéricos das colunas

Figura 2. Nomes de coluna de tipo específico e nomes de coluna genérica.
Classes de nome de coluna de tipo específico
A tabela 2 lista as classes de nome de coluna de tipo específico mais usadas:
Tabela 2. Classes de nome de coluna de tipo específico
Classes de mapeamento | Tipo de dados | Observações |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
São os dados de nome do contato bruto associados a essa linha de dados. | Os contatos brutos têm somente uma dessas linhas. |
ContactsContract.CommonDataKinds.Photo |
É a foto principal do contato bruto associada a essa linha de dados. | Os contatos brutos têm somente uma dessas linhas. |
ContactsContract.CommonDataKinds.Email |
É um endereço de e-mail do contato bruto associado a essa linha de dados. | Os contatos brutos podem ter diversos endereços de e-mail. |
ContactsContract.CommonDataKinds.StructuredPostal |
É um endereço postal do contato bruto associado a essa linha de dados. | Os contatos brutos podem ter diversos endereços postais. |
ContactsContract.CommonDataKinds.GroupMembership |
É um identificador que vincula o contato bruto a um dos grupos no Provedor de contatos. | Grupos são um recurso opcional de um tipo e um nome de conta. Elas estão descritas em Veja mais detalhes na seção Grupos de contatos. |
Contatos
O Provedor de Contatos combina as linhas do contato bruto entre todos os tipos e nomes de conta para formar um contato. Isso facilita a exibição e modificação de todos os dados que um que o usuário coletou para uma pessoa. O Provedor de contatos gerencia a criação de novos contatos linhas e a agregação de contatos brutos a uma linha de contato existente. Nem os aplicativos nem os adaptadores de sincronização podem adicionar contatos, e algumas colunas em uma linha de contato são somente leitura.
Observação:se você tentar adicionar um contato ao Provedor de contatos com um
insert()
, você vai receber
uma exceção UnsupportedOperationException
. Se você tentar atualizar uma coluna
listado como "somente leitura", a atualização será ignorada.
O Provedor de Contatos cria um novo contato em resposta à adição de um novo contato bruto que não corresponda a nenhum contato existente. O provedor também faz isso se os dados de um contato bruto existente mudam de modo a não corresponder mais ao contato a que ele estava associado anteriormente. Se um aplicativo ou adaptador de sincronização criar um novo contato bruto que corresponder a um contato existente, o novo contato bruto será agregado ao contato existente contato
O Provedor de Contatos vincula uma linha do contato às linhas do contato bruto com a coluna
_ID
da linha do contato na tabela
Contacts
. A coluna CONTACT_ID
da tabela de contatos brutos
ContactsContract.RawContacts
contém _ID
valores para
a linha de contatos associada a cada linha de contatos brutos.
A tabela ContactsContract.Contacts
também tem a coluna
LOOKUP_KEY
, que é um
link "permanente" para a linha do contato. Como o Provedor de Contatos mantém contatos
automaticamente, ele pode mudar o valor de _ID
de uma linha de contato
em resposta a uma agregação ou sincronização. Mesmo que isso aconteça, o URI de conteúdo
CONTENT_LOOKUP_URI
combinado com
LOOKUP_KEY
do contato ainda vão
apontar para a linha de contato. Assim, você pode usar
LOOKUP_KEY
para manter links para os "favoritos" contatos e assim por diante. Essa coluna tem o próprio formato, que não tem nenhuma relação com o formato da coluna _ID
.
A figura 3 mostra como as três tabelas principais se relacionam entre si.

Figura 3. Contatos, contatos brutos e relacionamentos da tabela de detalhes.
Atenção: se você publicar seu app na Google Play Store ou se ele estiver em um dispositivo com o Android 10 (nível 29 da API) ou versões mais recentes, tenha em mente que um conjunto limitado de métodos e campos de dados de contatos estão obsoletos.
Nas condições mencionadas, o sistema limpa periodicamente todos os valores. gravados nesses campos de dados:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
As APIs usadas para definir os campos de dados citados acima também estão obsoletas:
Além disso, os campos a seguir não retornam mais os contatos frequentes. Observação que alguns desses campos influenciam as classificações de contatos somente quando contatos fazem parte de um grupo dados tipo.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(afeta apenas E-mail, Telefone, Chamável e Contatáveis tipos de dados) -
ENTERPRISE_CONTENT_FILTER_URI
(afeta apenas E-mail, Telefone, e Chamável tipos de dados)
Caso seus apps acessem ou atualizem esses campos ou APIs, use métodos. Por exemplo, é possível atender a certos casos de uso usando privado provedores de conteúdo ou outros dados armazenados no seu app ou back-end sistemas.
Para verificar se a funcionalidade do seu app não é afetada por essa mudança, faça o seguinte: pode limpar manualmente esses campos de dados. Para isso, execute o comando adb abaixo: em um dispositivo com o Android 4.1 (nível 16 da API) ou mais recente:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Dados de adaptadores de sincronização
Os usuários inserem dados de contato diretamente no dispositivo, mas os dados também são direcionados ao Provedor
de contatos de serviços Web via adaptadores de sincronização, que automatizam
a transferência de dados entre o dispositivo e os serviços. Adaptadores de sincronização são executados em segundo plano
sob o controle do sistema e chamam métodos ContentResolver
para gerenciar dados.
No Android, o serviço Web com que um adaptador de sincronização trabalha é identificado por um tipo de conta. Cada adaptador de sincronização funciona com um tipo de conta, mas é compatível com vários nomes de conta desse tipo. Tipos e nomes de contas são descritos brevemente na seção Fontes de dados de contatos brutos. As definições a seguir oferecem mais detalhes e descrevem como o tipo e o nome de conta se relacionam com adaptadores de sincronização e serviços.
- Tipo de conta
-
Identifica um serviço em que o usuário armazenou dados. Na maioria das vezes, o usuário precisa
se autenticar no serviço. Por exemplo, Contatos do Google é um tipo de conta, identificado
pelo código
google.com
. Esse valor corresponde ao tipo de conta usado porAccountManager
. - Nome da conta
- Identifica uma conta ou login específico de um tipo de conta. As contas Contatos do Google são idênticas às Contas do Google, que têm um endereço de e-mail como nome da conta. Outros serviços podem usar um nome de usuário com só uma palavra ou código numérico.
Os tipos de conta não precisam ser exclusivos. Um usuário pode configurar várias contas de Contatos do Google fazer o download dos dados para o Provedor de contatos; isso poderá acontecer se o usuário tiver um conjunto de contatos pessoais para um nome de conta pessoal e outro conjunto para trabalho. Os nomes das contas são geralmente exclusivos. Juntos, eles identificam um fluxo de dados específico entre o Provedor de contatos e um serviço externo.
Se você quiser transferir os dados do serviço ao Provedor de Contatos, precisará criar o próprio adaptador de sincronização. Isso é descrito com mais detalhes na seção Adaptadores de sincronização do Provedor de contatos.
A Figura 4 mostra como o Provedor de Contatos se insere no fluxo de dados sobre pessoas. Na caixa marcada como "adaptadores de sincronização", cada adaptador é rotulado pelo tipo de conta.

Figura 4. Fluxo de dados do Provedor de Contatos.
Permissões necessárias
Os aplicativos que queiram acessar o Provedor de Contatos precisam solicitar as seguintes permissões:
- Acesso de leitura a uma ou mais tabelas
-
READ_CONTACTS
, especificado emAndroidManifest.xml
com o elemento<uses-permission>
como<uses-permission android:name="android.permission.READ_CONTACTS">
. - Acesso de gravação a uma ou mais tabelas
-
WRITE_CONTACTS
, especificado emAndroidManifest.xml
com o elemento<uses-permission>
como<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Essas permissões não se estendem aos dados do perfil do usuário. O perfil do usuário e as permissões necessárias são abordadas na seção a seguir, O perfil de usuário.
Lembre-se de que os dados de contato do usuário são pessoais e sensíveis. Os usuários se preocupam com a privacidade e, por isso, não querem aplicativos que coletem dados sobre eles ou seus contatos. Se não for óbvio o motivo da necessidade de permissões para acessar os dados de contato de um usuário, eles podem atribuir classificações ruins ao seu aplicativo ou simplesmente não o instalar.
O perfil do usuário
A tabela ContactsContract.Contacts
tem uma única linha contendo
dados de perfil para o usuário do dispositivo. Esses dados descrevem a user
do dispositivo, em vez
de um dos contatos do usuário. A linha de contatos do perfil é vinculada a uma linha
de contatos brutos para cada sistema que usa um perfil.
Cada linha de contato bruto de perfil pode ter diversas linhas de dados. Constantes de acesso ao perfil
do usuário estão disponíveis na classe ContactsContract.Profile
.
O acesso ao perfil do usuário exige permissões especiais. Além das
permissões
READ_CONTACTS
e
WRITE_CONTACTS
necessárias para ler e gravar, o acesso
ao perfil do usuário requer as permissões android.Manifest.permission#READ_PROFILE e
android.Manifest.permission#WRITE_PROFILE para acesso de leitura e gravação,
respectivamente.
Lembre-se de que você deve considerar o perfil do usuário como confidencial. A permissão android.Manifest.permission#READ_PROFILE permite acessar os dados de identificação pessoal do usuário do dispositivo. Explique ao usuário o motivo você precisa de permissões de acesso ao perfil de usuário na descrição do seu aplicativo.
Para recuperar a linha de contato que contém o perfil do usuário,
chame ContentResolver.query()
. Defina o URI de conteúdo como
CONTENT_URI
e não forneça nenhum
critério de seleção. Também é possível usar esse URI de conteúdo como base para recuperar contatos brutos
ou dados do perfil. Por exemplo, esse snippet recupera dados do 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);
Observação: se você recuperar várias linhas de contato e quiser determinar se uma delas
é o perfil do usuário, teste a classe
IS_USER_PROFILE
. Essa coluna
será definida como "1" se o contato for o perfil do usuário.
Metadados do Provedor de contatos
O Provedor de Contatos gerencia dados que acompanham o estado dos dados de contatos no
repositório. Esses metadados sobre o repositório são armazenados em vários locais, incluindo
as linhas da tabela "Contatos brutos", "Dados" e "Contatos", a
tabela ContactsContract.Settings
, e a
ContactsContract.SyncState
. A tabela a seguir mostra o
efeito de cada uma dessas partes de metadados:
Tabela 3. Metadados no Provedor de Contatos
Tabela | Coluna | Valores | Significado |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0": sem modificação desde a última sincronização. |
Sinaliza contatos brutos que foram alterados no dispositivo e precisam ser sincronizados com o
servidor. O valor é definido automaticamente pelo Provedor de contatos quando o Android
aplicativos atualizam uma linha.
Adaptadores de sincronização que modificam o contato bruto ou as tabelas de dados devem sempre anexar a propriedade
string |
"1": modificado desde a última sincronização. Precisa ser sincronizado com o servidor. | |||
ContactsContract.RawContacts |
VERSION |
É o número da versão dessa linha. | O Provedor de contatos incrementa esse valor automaticamente sempre que a linha ou e as mudanças nos dados relacionados. |
ContactsContract.Data |
DATA_VERSION |
É o número da versão dessa linha. | O Provedor de contatos incrementa esse valor automaticamente sempre que a linha de dados é alterado. |
ContactsContract.RawContacts |
SOURCE_ID |
Valor de string que identifica exclusivamente esse contato bruto para a conta em que foi criado. |
Quando um adaptador de sincronização cria um novo contato bruto, essa coluna deve ser definida como o
ID exclusivo do servidor para o contato bruto. Quando um aplicativo Android cria
contato bruto, o aplicativo deve deixar a coluna vazia. Isso sinaliza que a sincronização
que ele deve criar um novo contato bruto no servidor e obter uma
para o SOURCE_ID .
Especificamente, o ID de origem precisa ser exclusivo para cada conta. e precisa ser estável entre as sincronizações:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" - Os contatos neste grupo não devem ser visíveis nas interfaces do aplicativo Android. | Esta coluna se destina à compatibilidade com servidores que permitem que um usuário oculte contatos no a determinados grupos. |
"1": os contatos nesse grupo podem ser visíveis nas IUs do aplicativo. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" - Para essa conta e esse tipo de conta, os contatos que não pertencem a um grupo são invisível para as IUs de aplicativos Android. |
Por padrão, os contatos são invisíveis se nenhum dos contatos brutos pertence a um grupo
(a associação de um contato bruto a grupos é indicada por uma ou mais
linhas ContactsContract.CommonDataKinds.GroupMembership
na tabela ContactsContract.Data ).
Ao definir essa flag na linha ContactsContract.Settings da tabela,
para um tipo de conta e uma conta, é possível forçar a visibilidade dos contatos sem grupos.
Esse sinalizador serve para mostrar contatos de servidores que não usam grupos.
|
"1" - Para essa conta e esse tipo de conta, os contatos que não pertencem a um grupo são visíveis para as IUs do aplicativo. | |||
ContactsContract.SyncState |
(todos) | Use essa tabela para armazenar metadados do seu adaptador de sincronização. | Com essa tabela, você pode armazenar o estado de sincronização e outros dados relacionados à sincronização de forma persistente em o dispositivo. |
Acesso ao Provedor de contatos
Esta seção descreve diretrizes para acessar dados do Provedor de contatos, com foco em o seguinte:
- Consultas de entidade.
- Modificação em lote.
- Recuperação e modificação com intents.
- Integridade dos dados.
A realização de modificações de um adaptador de sincronização também é abordada na seção Adaptadores de sincronização do Provedor de contatos.
Consultas de entidades
A organização hierárquica das tabelas do Provedor de Contatos é muito útil para
recuperar uma linha e todas as linhas "filhas" vinculadas. Por exemplo, para exibir
todas as informações de uma pessoa, recupere todas as
ContactsContract.RawContacts
linhas para uma única
ContactsContract.Contacts
ou todas as
ContactsContract.CommonDataKinds.Email
linhas para uma única
ContactsContract.RawContacts
. Para facilitar isso, o Provedor de contatos
oferece a ideia de entidade, que atua como uma junção dos bancos de dados entre
tabelas.
Uma entidade é como uma tabela composta de colunas selecionadas de uma tabela pai e uma tabela filha.
Ao consultar uma entidade, fornece-se uma projeção e buscam-se critérios com base nas colunas
disponíveis da entidade. O resultado é um Cursor
que contém
uma linha para cada linha da tabela filha que foi recuperada. Por exemplo, se você consultar
ContactsContract.Contacts.Entity
para o nome de um contato
e todas as linhas ContactsContract.CommonDataKinds.Email
para todos os
contatos brutos com esse nome, você recebe uma Cursor
contendo uma linha
para cada linha ContactsContract.CommonDataKinds.Email
.
As entidades simplificam as consultas. Usando uma entidade, você pode recuperar todos os dados de contatos de um contato bruto ou contato bruto de uma só vez, em vez de ter que consultar primeiro a tabela pai para obter uma ID e, em seguida, ter que consultar a tabela filho com esse ID. Além disso, o Provedor de contatos processa uma consulta em uma entidade em uma única transação, o que garante que os dados recuperados sejam com consistência interna.
Observação: uma entidade normalmente não contém todas as colunas da mãe e
tabela filha. Se você tentar trabalhar com um nome de coluna que não esteja na lista de constantes de nomes de coluna da entidade, será gerada uma Exception
.
O snippet a seguir mostra como recuperar todas as linhas de contato bruto de um contato. O snippet
faz parte de um aplicativo maior que tem duas atividades, a “principal” e "detalhe". A atividade principal
mostra uma lista de linhas de contato. Quando um usuário seleciona uma delas, a atividade envia o ID correspondente à atividade
de detalhes. A atividade detalhada usa o ContactsContract.Contacts.Entity
.
para exibir todas as linhas de dados de todos os contatos brutos associados aos contatos
contato
Esse snippet é extraído da estrutura atividade:
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. }
Quando o carregamento é concluído, LoaderManager
invoca um callback para
onLoadFinished()
. Um dos argumentos de entrada para esse método é um
Cursor
com os resultados da consulta. No seu próprio app, você pode receber os
dados desse Cursor
para exibi-los ou trabalhar com eles posteriormente.
Modificação em lote
Sempre que possível, insira, atualize e exclua dados no Provedor de contatos em
"modo de lote", criando uma ArrayList
de
objetos ContentProviderOperation
e chamando
applyBatch()
. Como
o Provedor de contatos realiza todas as operações em um
applyBatch()
em uma única
transação, as modificações nunca deixarão o repositório de contatos em um estado
inconsistente. As modificações em lote também facilitam a inserção de um contato bruto e seus dados de detalhe ao
mesmo tempo.
Observação: para modificar um único contato bruto, envie uma intent para no aplicativo de contatos do dispositivo em vez de processar a modificação no aplicativo. Isso é descrito em mais detalhes na seção Recuperação e modificação com intents.
Pontos de rendimento
As modificações em lote que contiverem muitas operações podem bloquear outros processos,
resultando em uma experiência geral ruim para o usuário. Para organizar todas as modificações que você quer
realizar no menor número possível de listas separadas e, ao mesmo tempo, evitar que
que estão bloqueando o sistema, você deve definir pontos de rendimento para uma ou mais operações.
Um ponto de rendimento é um objeto ContentProviderOperation
que tem o
valor isYieldAllowed()
definido como
true
. Quando o Provedor de contatos encontra um ponto de rendimento, ele pausa o trabalho para
deixar outros processos serem executados e fechar a transação atual. Quando o provedor retorna, ele
continua na próxima operação da ArrayList
e inicia uma nova
transação.
Os pontos de rendimento resultam em mais de uma transação por chamada para
applyBatch()
: Por isso,
é preciso definir um ponto de rendimento para a última operação de um conjunto de linhas relacionadas.
Por exemplo, é preciso definir um ponto de rendimento para a última operação em um conjunto que adicione
linhas de um contato bruto e linhas de dados associados a ele, ou para a última operação de um conjunto de linhas relacionadas
a um único contato.
Os pontos de rendimento também são uma unidade de operação atômica. Todos os acessos entre dois pontos de rendimento bem-sucedidos ou não como uma única unidade. Se você não definir pontos de rendimento, o menor operação atômica é o lote inteiro de operações. Se forem usados pontos de rendimento, eles evitarão que as operações prejudiquem o desempenho do sistema e, ao mesmo tempo, garantirão que o subconjunto de operações seja atômico.
Referências de retorno da modificação
Ao inserir uma nova linha de contato bruto e as linhas de dados associados como um conjunto de
objetos ContentProviderOperation
, é preciso vincular as linhas de dados à
linha de contato bruto pela inserção do valor
_ID
do contato bruto como o
valor RAW_CONTACT_ID
. No entanto, esse
valor não está disponível ao criar a ContentProviderOperation
para a linha de dados porque você ainda não aplicou a
ContentProviderOperation
para a linha de contato bruto. Para contornar esse problema,
a classe ContentProviderOperation.Builder
tem o método
withValueBackReference()
.
Esse método permite a inserção ou modificação de uma coluna com o
resultado de uma operação anterior.
O withValueBackReference()
tem dois argumentos:
-
key
- É a chave de um par de chave-valor. O valor desse argumento precisa ser o nome de uma coluna na tabela que será modificada.
-
previousResult
-
É o índice com base 0 de um valor na matriz de
objetos
ContentProviderResult
deapplyBatch()
. Conforme operações em lote forem aplicadas, o resultado de cada operação será armazenado em matriz intermediária de resultados. O valorpreviousResult
é o índice de um desses resultados, que é recuperado e armazenado com o valorkey
. Isso permite que você insira um novo registro de contato bruto e recupere seu_ID
e faça uma "referência de retorno" ao quando você adiciona uma linhaContactsContract.Data
.Toda a matriz de resultados é criada quando você chama
applyBatch()
, com um tamanho igual ao tamanho daArrayList
doContentProviderOperation
objetos fornecidos por você. No entanto, todas os elementos na matriz de resultados são definidos comonull
e, se você tentar para fazer uma referência retroativa a um resultado de uma operação que ainda não foi aplicada,withValueBackReference()
gera umaException
.
Os snippets a seguir mostram como inserir um novo contato bruto e dados em lote. Eles incluem o código que estabelece um ponto de rendimento e usa uma referência de retorno.
O primeiro snippet recupera dados de contato da IU. Nesse momento, o usuário já selecionou a conta a que o novo contato bruto deve ser adicionado.
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());
O próximo snippet cria uma operação para inserir a linha de contato bruto na
tabela 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());
Em seguida, o código cria linhas de dados para as linhas de nome de exibição, telefone e e-mail.
Cada objeto construtor de operações usa
withValueBackReference()
para receber o
RAW_CONTACT_ID
. Os pontos de referência
voltam ao objeto ContentProviderResult
da primeira operação,
que adiciona a linha de contato bruto e retorna o novo valor
_ID
. Como resultado, cada linha de dados é automaticamente vinculada por meio do
RAW_CONTACT_ID
à nova linha ContactsContract.RawContacts
a que ela pertence.
O objeto ContentProviderOperation.Builder
que adiciona a linha de e-mail é
sinalizado com withYieldAllowed()
, que define um ponto de rendimento:
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());
O último snippet mostra a chamada para
applyBatch()
que
insere as novas linhas de contato bruto e de dados.
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); } }
As operações em lote também permitem implementar controle otimista de simultaneidade, um método de aplicar transações de modificação sem precisar bloquear o repositório subjacente. Para usar esse método, aplique a transação e, em seguida, verifique se há outras modificações que podem ter sido feitas ao mesmo tempo. Se ficar determinado que houve uma modificação incoerente, reverte-se a transação e tenta-se novamente.
O controle otimista de simultaneidade é útil para dispositivos móveis em que haja somente um usuário por vez e que sejam raros os acessos simultâneos a um repositório de dados. Como os bloqueios não são usados, não há tempo gasto em bloqueios de configuração nem espera para que outras transações liberem os respectivos bloqueios.
Para usar o controle de simultaneidade otimista ao atualizar uma única
ContactsContract.RawContacts
, siga estas etapas:
-
Recupere a coluna
VERSION
do contato bruto em conjunto com os outros dados recuperados. -
Crie um objeto
ContentProviderOperation.Builder
adequado para aplicar uma restrição usando o métodonewAssertQuery(Uri)
. Para o URI de conteúdo, usarRawContacts.CONTENT_URI
com o_ID
do contato bruto anexado a ele. -
Para o objeto
ContentProviderOperation.Builder
, chamewithValue()
para comparar osVERSION
ao número da versão que você acabou de recuperar. -
Para o mesmo
ContentProviderOperation.Builder
, chamewithExpectedCount()
para garantir que apenas uma linha seja testada por essa declaração. -
Chame
build()
para criar o objetoContentProviderOperation
e, em seguida, adicione esse objeto como o primeiro objeto naArrayList
que você vai transmitir paraapplyBatch()
. - Aplique a transação em lote.
Se a linha do contato bruto for atualizada por outra operação entre o momento da leitura da linha e
momento em que tenta modificá-la, o atributo "assert" ContentProviderOperation
vai falhar, e o backup de todo o lote de operações será feito. Em seguida, tente novamente
o lote ou realizar alguma outra ação.
O snippet a seguir demonstra como criar uma ContentProviderOperation
"assert" após consultar um único contato bruto usando
um 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 }
Recuperação e modificação com intents
O envio de um intent ao aplicativo de contatos do dispositivo permite que você acesse os Contatos o provedor indiretamente. O intent inicia a IU do aplicativo de contatos do dispositivo, em que os usuários podem fazer tarefas relacionadas a contatos. Com esse tipo de acesso, os usuários podem:
- Selecionar um contato de uma lista e retorná-lo ao aplicativo para trabalhos futuros.
- Editar os dados de um contato.
- Inserir um novo contato bruto para quaisquer das suas contas.
- Excluir um contato ou dados dos contatos.
Se o usuário estiver inserindo ou atualizando dados, você poderá coletar os dados primeiro e enviá-los como parte da intent.
Ao usar intents para acessar o Provedor de contatos pelo aplicativo de contatos do dispositivo, você não precisa escrever sua própria IU ou código para acessar o provedor. Também não é necessário solicitar permissão de leitura e gravação ao provedor. O aplicativo de contatos do dispositivo pode delegar permissões de leitura de um contato e, pelo fato de você fazer modificações no provedor por meio de outro aplicativo, não é necessário ter permissões de gravação.
O processo geral de envio de um intent para acessar um provedor é descrito detalhadamente no guia
Fundamentos do provedor de conteúdo na seção "Acesso a dados via intents". A ação,
O tipo MIME e os valores de dados usados para as tarefas disponíveis estão resumidos na Tabela 4, enquanto o
valores extras que podem ser usados com
putExtra()
estão listados no
documentação de referência para ContactsContract.Intents.Insert
:
Tabela 4. Intenções do Provedor de Contatos
Tarefa | Ação | Dados | Tipo MIME | Observações |
---|---|---|---|---|
Escolher um contato de uma lista | ACTION_PICK |
Uma destas:
|
Não usado |
Mostra uma lista de contatos brutos ou uma lista de dados de um contato bruto, dependendo do
tipo de URI de conteúdo fornecido.
Ligação
|
Inserir um novo contato bruto | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE , tipo MIME para um conjunto de contatos brutos.
|
Exibe a tela Adicionar contato do aplicativo de contatos do dispositivo. Os
valores extras adicionados ao intent são exibidos. Se enviado com
startActivityForResult() ,
o URI de conteúdo do contato bruto recém-adicionado é transmitido de volta ao
onActivityResult()
callback no argumento Intent , na classe
“dados” . Para receber o valor, chame getData() .
|
Editar um contato | ACTION_EDIT |
CONTENT_LOOKUP_URI do
contato. A atividade do editor permitirá que o usuário edite os dados associados
a esse contato.
|
Contacts.CONTENT_ITEM_TYPE , um único contato. |
Exibe a tela “Edit Contact” no aplicativo de contatos. Os valores extras adicionados ao intent são mostrados. Quando o usuário clica em Concluído para salvar o edições, sua atividade retorna para o primeiro plano. |
Mostrar um seletor que também pode adicionar dados | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Essa intent sempre exibe a tela do seletor do app Contatos. O usuário pode
escolha um contato para editar ou adicione um novo contato. A tela de edição ou de adição
aparece, dependendo da escolha do usuário, e os dados extras transmitidos na intent
são exibidos. Se o app mostra dados de contato, como um e-mail ou número de telefone, use
essa intent para permitir que o usuário adicione os dados a um contato existente.
contato,
Observação:não é necessário enviar um valor de nome nos extras dessa intent. porque o usuário sempre escolhe um nome existente ou adiciona um novo. Além disso, Se você enviar um nome e o usuário optar por editar, o app Contatos exibirá o nome enviado, substituindo o valor anterior. Se o usuário não perceber isso e salvar a edição, o valor antigo será perdido. |
O app de contatos do dispositivo não permite a exclusão de um contato bruto ou de seus dados com um
intent. Em vez disso, para excluir um contato bruto, use
ContentResolver.delete()
ou ContentProviderOperation.newDelete()
.
O snippet a seguir mostra como construir e enviar uma intent que insere um novo arquivo bruto contato e dados:
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);
Integridade dos dados
Como o repositório de contatos contém dados importantes e confidenciais que os usuários esperam que estejam corretos e atualizados, o Provedor de Contatos tem regras bem definidas para a integridade dos dados. Está sua responsabilidade de obedecer a essas regras ao modificar dados de contatos. As mais importantes regras estão listadas aqui:
-
Sempre adicione uma linha
ContactsContract.CommonDataKinds.StructuredName
para cadaContactsContract.RawContacts
linha adicionada. -
Uma linha
ContactsContract.RawContacts
sem uma linhaContactsContract.CommonDataKinds.StructuredName
na tabelaContactsContract.Data
pode causar problemas durante a agregação. -
Sempre conecte novas linhas
ContactsContract.Data
à linhaContactsContract.RawContacts
mãe. -
Uma linha
ContactsContract.Data
que não está vinculada a umaContactsContract.RawContacts
não ficará visível no dispositivo de contatos, e isso pode causar problemas com adaptadores de sincronização. - Altere dados somente para seus contatos brutos.
- Lembre-se de que o Provedor de contatos normalmente gerencia dados de vários tipos de conta/serviços on-line. É preciso garantir que o aplicativo modifique ou exclua somente dados de linhas de sua propriedade e que ele insira dados apenas com um tipo e nome de conta que você controla.
-
Sempre use as constantes definidas em
ContactsContract
e suas subclasses de autoridades, URIs de conteúdo, caminhos de URI, nomes de coluna, tipos MIME e valoresTYPE
. - O uso dessas constantes ajuda a evitar erros. Você também vai receber notificações com avisos do compilador se uma das constantes estiver obsoleta.
Linhas de dados personalizados
Ao criar e usar seus próprios tipos MIME personalizados, você pode inserir, editar, excluir e recuperar
suas linhas de dados na tabela ContactsContract.Data
. Suas linhas
são limitadas a usar a coluna definida
ContactsContract.DataColumns
, embora você possa mapear as suas próprias
nomes de coluna de tipo específico aos nomes de coluna padrão. No aplicativo de contatos do dispositivo,
os dados das linhas são exibidos, mas não podem ser editados nem excluídos, e os usuários não podem adicionar
dados adicionais. Para permitir que os usuários modifiquem suas linhas de dados personalizadas, forneça um editor
atividades em seu próprio aplicativo.
Para exibir seus dados personalizados, forneça um arquivo contacts.xml
que contenha um
<ContactsAccountType>
e um ou mais dos
<ContactsDataKind>
elementos filhos. Isso é descrito com mais detalhes nas
seção <ContactsDataKind> element
.
Para saber mais sobre tipos MIME personalizados, leia o guia Criar um provedor de conteúdo.
Adaptadores de sincronização do Provedor de contatos
O Provedor de contatos foi projetado especificamente para lidar com a sincronização de dados de contatos entre um dispositivo e um serviço on-line. Isso permite que os usuários façam o download de dados existentes em um novo dispositivo e os enviem para uma nova conta. A sincronização também garante que os usuários tenham os dados mais recentes à mão, independentemente da origem de adições e alterações. Outra vantagem da sincronização é a disponibilidade dos dados dos contatos mesmo quando o dispositivo não está conectado à rede.
Embora seja possível implementar a sincronização de várias formas, o sistema Android oferece uma estrutura de sincronização de plug-ins que automatiza as seguintes tarefas:
- Verificação da disponibilidade da rede.
- Agendamento e execução de sincronizações, com base nas preferências do usuário.
- Reinicialização de sincronizações que foram interrompidas.
Para usar essa biblioteca, deve-se fornecer um plug-in do adaptador de sincronização. Cada adaptador de sincronização é exclusivo de um serviço e um provedor de conteúdo, mas pode processar diversos nomes de conta do mesmo serviço. A estrutura também permite vários adaptadores de sincronização para o mesmo serviço e provedor.
Classes e arquivos do adaptador de sincronização
O adaptador de sincronização é implementado como uma subclasse
AbstractThreadedSyncAdapter
e instale-o como parte de uma
para o aplicativo. O sistema aprende sobre o adaptador de sincronização pelos elementos do aplicativo
e de um arquivo XML especial para onde o manifesto aponta. O arquivo XML define o
tipo de conta do serviço on-line e a autoridade do provedor de conteúdo que, juntos,
identificam exclusivamente o adaptador. O adaptador de sincronização não fica ativo até que o usuário adicione um
considerar o tipo de conta do adaptador de sincronização e ativar a sincronização do conteúdo
provedor com que o adaptador de sincronização é sincronizado. Nesse ponto, o sistema começa a gerenciar o adaptador,
chamá-lo conforme necessário para sincronizar entre o provedor de conteúdo e o servidor.
Observação: usar um tipo de conta como parte da identificação do adaptador de sincronização permite
que o sistema detecte e agrupe adaptadores de sincronização que acessam diferentes serviços
na mesma organização. Por exemplo, todos os adaptadores de sincronização dos serviços on-line do Google têm o mesmo
tipo de conta com.google
. Quando os usuários adicionam uma Conta do Google aos dispositivos, todos
os adaptadores de sincronização instalados para serviços do Google estão listados juntos; cada adaptador de sincronização
a sincronização listada com um provedor de conteúdo diferente no dispositivo.
Como a maioria dos serviços exige que os usuários verifiquem sua identidade antes de acessar
dados, o sistema Android oferece um framework de autenticação semelhante e, muitas vezes,
usado em conjunto com o framework do adaptador de sincronização. A estrutura de autenticação usa
autenticadores de plug-in que são subclasses de
AbstractAccountAuthenticator
. Um autenticador verifica
a identidade do usuário nas seguintes etapas:
- Coleta nome, senha ou informações semelhantes do usuário (o endereço credenciais).
- Envia as credenciais para o serviço.
- Examina a resposta do serviço.
Se o serviço aceitar as credenciais, o autenticador poderá
armazená-las para uso futuro. Devido à estrutura do autenticador de plug-ins,
AccountManager
pode conferir acesso a quaisquer tokens de autenticação compatíveis com um autenticador
que escolha expô-los, como os tokens de autenticação OAuth2.
Embora as autenticações não sejam necessárias, a maioria dos serviços de contato as usam. No entanto, não é obrigatório usar a estrutura de autenticação do Android para fazer a autenticação.
Implementação do adaptador de sincronização
Para implementar um adaptador de sincronização para o Provedor de Contatos, primeiro é necessário criar um aplicativo Android que contenha o seguinte:
-
Um componente
Service
que responde a solicitações do sistema para vincular ao adaptador de sincronização. -
Quando o sistema quer fazer uma sincronização, ele chama o serviço
método
onBind()
para receberIBinder
para o adaptador de sincronização. Isso permite que o sistema faça chamadas entre processos para os métodos do adaptador. -
O adaptador de sincronização atual, implementado como uma subclasse concreta de
AbstractThreadedSyncAdapter
. -
Essa classe realiza o trabalho de baixar dados do servidor, carregar dados do
dispositivo e resolver conflitos. O principal trabalho do adaptador
feita no método
onPerformSync()
. Essa classe deve ser instanciada como um singleton. -
Uma subclasse de
Application
. -
Essa classe atua como uma fábrica para o singleton do adaptador de sincronização. Use o
Método
onCreate()
para instanciar o adaptador de sincronização. fornecem um "getter" estático para retornar o singleton aoonBind()
do método serviço. -
Opcional: um componente
Service
que responde a solicitações do sistema para autenticação do usuário. -
AccountManager
inicia esse serviço para iniciar o processo de autenticação. O métodoonCreate()
do serviço instancia um objeto autenticador. Quando o sistema deseja autenticar uma conta de usuário para o adaptador de sincronização do aplicativo, ele chama o serviço métodoonBind()
para receber umaIBinder
para o autenticador. Isso permite que o sistema faça chamadas entre processos para os métodos do autenticador. -
Opcional: uma subclasse concreta de
AbstractAccountAuthenticator
que processa solicitações de autenticação. -
Essa classe fornece métodos que o
AccountManager
invoca para autenticar as credenciais do usuário no servidor. Os detalhes desse processo de autenticação variam amplamente, com base na tecnologia em uso no servidor. Você deve consulte a documentação do seu software servidor para saber mais sobre autenticação. - Os arquivos XML que definem o adaptador de sincronização e o autenticador para o sistema.
-
Os componentes de serviço do adaptador de sincronização e do autenticador descritos anteriormente são
definido em
<service>
no manifesto do aplicativo. Esses elementos contêm elementos filhos<meta-data>
que fornecem dados específicos ao sistema:-
O
<meta-data>
para os pontos de serviço do adaptador de sincronização para o Arquivo XMLres/xml/syncadapter.xml
. Em troca, esse arquivo especifica um URI para o serviço da Web que será sincronizado com o Provedor de contatos e um tipo de conta. -
Opcional: o elemento
<meta-data>
do autenticador aponta para o arquivo XMLres/xml/authenticator.xml
. Em troca, esse arquivo especifica o tipo de conta compatível com esse autenticador, bem como recursos de IU que aparecem durante o processo de autenticação. O tipo de conta especificado deve ser o mesmo do tipo de conta especificado para a sincronização por um adaptador.
-
O
Dados de streams sociais
As solicitações android.provider.ContactsContract.StreamItems e Tabelas android.provider.ContactsContract.StreamItemPhotos gerenciar dados recebidos de redes sociais. É possível criar um adaptador de sincronização que adicione dados de stream da sua própria rede para essas tabelas, ou pode ler dados de fluxo dessas tabelas e exibi-los em seu próprio aplicativo ou ambos. Com esses recursos, os serviços e aplicativos de redes sociais podem ser integrados na experiência das redes sociais no Android.
Textos de streams sociais
Itens de stream sempre são associados a um contato bruto. O
android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID se vincula ao
Valor _ID
do contato bruto. O tipo e o nome da conta do contato bruto
também são armazenados na linha do item de fluxo.
Armazene os dados do seu stream nas colunas a seguir:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Obrigatório. É o tipo de conta do usuário do contato bruto associado a este item de stream. Lembre-se de definir esse valor ao inserir um item de stream.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Obrigatório. É o nome de conta do usuário do contato bruto associado a essa item de stream. Lembre-se de definir esse valor ao inserir um item de stream.
- Colunas identificadoras
-
Obrigatório. Você precisa inserir as seguintes colunas de identificador ao
para inserir um item de stream:
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: O Valor android.provider.BaseColumns#_ID do contato a que esse stream está associado.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: O android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY valor do contato a que este item de fluxo está associado.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: O Valor android.provider.BaseColumns#_ID do contato bruto que esse stream está associado.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Opcional. Armazena informações resumidas que podem ser exibidas no início do item de stream.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
É o texto do item de stream, o conteúdo que foi publicado pela fonte do item
ou uma descrição de alguma ação que gerou o item de stream. Essa coluna pode conter
qualquer formatação e imagens de recurso incorporadas que possam ser renderizadas por
fromHtml()
. O provedor pode truncar ou abreviar conteúdos longos, mas ele tentará evitar quebrar as tags. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- É uma string de texto contendo o tempo em que o item de stream foi inserido ou atualizado, em forma milissegundos desde a época. Os aplicativos que inserem ou atualizam itens de fluxo são responsável por manter esta coluna; ela não é mantida automaticamente Provedor de contatos.
Para mostrar informações de identificação dos itens de stream, use android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL e android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE para vincular a recursos no aplicativo.
A tabela android.provider.ContactsContract.StreamItems também contém as colunas. de android.provider.ContactsContract.StreamItemsColumns#SYNC1 a android.provider.ContactsContract.StreamItemsColumns#SYNC4 para uso exclusivo de e adaptadores de sincronização.
Fotos de streams sociais
A tabela android.provider.ContactsContract.StreamItemPhotos armazena fotos associadas
a um item de fluxo. O valor
Coluna android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID
vincula aos valores na coluna _ID
de
Tabela android.provider.ContactsContract.StreamItems. As referências de fotos são armazenadas
nestas colunas:
- Coluna android.provider.ContactsContract.StreamItemPhotos#PHOTO (um BLOB).
- Representação binária da foto, redimensionada pelo provedor para armazenamento e exibição. Essa coluna está disponível para compatibilidade retroativa com versões anteriores do Provedor de Contatos que a usavam para armazenar fotos. No entanto, na versão atual, não use essa coluna para armazenar fotos. Em vez disso, use android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID ou android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (ambos os que são descritos nos pontos a seguir) para armazenar fotos em um arquivo. Essa coluna passa a conter uma miniatura da foto, que estará disponível para leitura.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Identificador numérico de uma foto de um contato bruto. Anexar esse valor à constante
DisplayPhoto.CONTENT_URI
para obter um URI de conteúdo que aponte para um único arquivo de foto e, em seguida, chameopenAssetFileDescriptor()
para gerar um identificador do arquivo de foto. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
É um URI de conteúdo direcionado diretamente para o arquivo de foto da foto representada por essa linha.
Chame
openAssetFileDescriptor()
com esse URI para receber um identificador para o arquivo de foto.
Uso de tabelas de streams sociais
Essas tabelas funcionam como as outras tabelas principais do Provedor de Contatos, exceto que:
- exigem permissões de acesso adicionais. Para ler o conteúdo delas, o aplicativo precisa ter a permissão android.Manifest.permission#READ_SOCIAL_STREAM. Para modificá-las, o aplicativo precisa ter a permissão android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Para a tabela android.provider.ContactsContract.StreamItems, o número de linhas
armazenadas para cada contato bruto é limitada. Quando esse limite é atingido,
o Provedor de contatos abre espaço para novas linhas de itens de fluxo excluindo automaticamente
as linhas que têm os nomes
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. Para conseguir o
limite, faça uma consulta ao URI de conteúdo
android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. É possível deixar
todos os argumentos, exceto o URI de conteúdo, definidos como
null
. A consulta retorna um cursor contendo uma linha única com a coluna única android.provider.ContactsContract.StreamItems#MAX_ITEMS.
A classe android.provider.ContactsContract.StreamItems.StreamItemPhotos define um subtabela de android.provider.ContactsContract.StreamItemPhotos que contém a foto. linhas para um único item de fluxo.
Interações de streams sociais
Os dados de fluxos sociais gerenciados pelo Provedor de contatos, em conjunto com o aplicativo de contatos do dispositivo, oferece uma forma poderosa de conectar seu sistema de redes sociais com os contatos existentes. Os seguintes recursos estão disponíveis:
- Sincronizando seu serviço de rede social com o Provedor de contatos com uma sincronização. você pode recuperar atividades recentes dos contatos de um usuário e armazená-las em os objetos android.provider.ContactsContract.StreamItems e Tabelas android.provider.ContactsContract.StreamItemPhotos para uso posterior.
- Além da sincronização regular, é possível ativar o adaptador de sincronização para recuperar dados adicionais quando o usuário seleciona um contato para exibir. Isso permite que o adaptador de sincronização recupere fotos de alta resolução e os itens de stream mais recentes do contato.
- Ao registrar uma notificação com o aplicativo de contatos do dispositivo e o Provedor de Contatos, é possível receber uma intent quando um contato é exibido e, nesse ponto, atualizar o status do contato pelo serviço. Essa abordagem pode ser mais rápida e usar menos largura de banda do que fazer uma sincronização completa com um adaptador de sincronização.
- Os usuários podem adicionar um contato ao seu serviço de rede social enquanto o visualizam no aplicativo de contatos do dispositivo. É possível ativar esse recurso com o botão "convidar contato" recurso que pode ser ativado com uma combinação de atividade que adiciona um contato existente ao seu rede e um arquivo XML que fornece o aplicativo de contatos do dispositivo e a o Provedor de contatos com os detalhes do seu aplicativo.
A sincronização regular de itens de stream com o Provedor de Contatos é igual a outras sincronizações. Para saber mais sobre a sincronização, consulte a seção Adaptadores de sincronização do Provedor de contatos: Como registrar notificações e como convidar contatos são abordadas nas próximas duas seções.
Registro para processar exibições de redes sociais
Para registrar o adaptador de sincronização para receber notificações quando o usuário visualiza um contato que está gerenciados pelo adaptador de sincronização:
-
Crie um arquivo chamado
contacts.xml
no diretóriores/xml/
do projeto. Se você já tiver esse arquivo, pule esta etapa. -
Nesse arquivo, adicione o elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
: Se esse elemento já existir, pule esta etapa. -
Para registrar um serviço que seja notificado quando o usuário abrir a página de detalhes de um contato no
aplicativo de contatos do dispositivo, adicione o atributo
viewContactNotifyService="serviceclass"
ao elemento, em queserviceclass
é o nome de classe totalmente qualificado do serviço que deve receber a intent do aplicativo de contatos do dispositivo. Para o serviço de notificação, use uma classe que estendeIntentService
para permitir que o serviço receba intents. Os dados na intent recebida contêm o URI de conteúdo dos dados brutos contato em que o usuário clicou. No serviço de notificação, é possível vincular e chamar o adaptador de sincronização para atualizar os dados do contato bruto.
Para registrar uma atividade a ser chamada quando o usuário clica em um item de stream, em uma foto ou em ambos:
-
Crie um arquivo chamado
contacts.xml
nores/xml/
do seu projeto. diretório. Se você já tiver esse arquivo, pule esta etapa. -
Nesse arquivo, adicione o elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
: Se esse elemento já existir, pule esta etapa. -
Para registrar uma das atividades para processar o usuário que clica em um item de stream no
aplicativo de contatos do dispositivo, adicione o atributo
viewStreamItemActivity="activityclass"
ao elemento, em queactivityclass
é o nome de classe totalmente qualificado da atividade que deve receber a intent do aplicativo de contatos do dispositivo. -
Para registrar uma das atividades para processar o usuário que clica em uma foto de stream no
aplicativo de contatos do dispositivo, adicione o atributo
viewStreamItemPhotoActivity="activityclass"
ao elemento, em queactivityclass
é o nome de classe totalmente qualificado da atividade que deve receber a intent do aplicativo de contatos do dispositivo.
O elemento <ContactsAccountType>
é descrito em mais detalhes nas
seção <ContactsAccountType> elemento.
O intent recebido contém o URI de conteúdo do item ou foto em que o usuário clicou. Para ter atividades separadas para itens de texto e para fotos, use os dois atributos no mesmo arquivo.
Interação com o serviço de redes sociais
Os usuários não precisam sair do aplicativo de contatos do dispositivo para convidar um contato para sua rede social de rede social. Em vez disso, você pode fazer com que o aplicativo de contatos do dispositivo envie uma intenção de convidar o contato a uma de suas atividades. Para definir essa configuração:
-
Crie um arquivo chamado
contacts.xml
no diretóriores/xml/
do projeto. Se você já tiver esse arquivo, pule esta etapa. -
Nesse arquivo, adicione o elemento
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Se esse elemento já existir, pule esta etapa. -
Adicione os seguintes atributos:
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
é o nome de classe totalmente qualificado da atividade que deve receber a intent. O valorinvite_action_label
é uma string de texto exibida no menu Add Connection no aplicativo de contatos do dispositivo.
Observação: ContactsSource
é um nome de tag descontinuado para
ContactsAccountType
.
Referência do contacts.xml
O arquivo contacts.xml
contém elementos XML que controlam a interação do
adaptador de sincronização e do aplicativo com o aplicativo de contatos e o Provedor de Contatos. Esses
elementos são descritos nas seções a seguir.
<ContactsAccountType> elemento
O elemento <ContactsAccountType>
controla a interação das
com o aplicativo de contatos. Ele tem a seguinte sintaxe:
<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">
Contido em:
res/xml/contacts.xml
pode conter:
<ContactsDataKind>
Description:
Declara componentes e rótulos da IU do Android que permitem aos usuários convidar um dos seus contatos para uma rede social, notificar usuários quando um dos streams de redes sociais é atualizado etc.
O prefixo android:
do atributo não é necessário para os atributos
de <ContactsAccountType>
.
Atributos:
inviteContactActivity
- O nome de classe totalmente qualificado da atividade no aplicativo que você quer é ativado quando o usuário seleciona Adicionar conexão no aplicativo de contatos.
inviteContactActionLabel
-
Uma string de texto exibida para a atividade especificada em
inviteContactActivity
, no menu Add connection. Por exemplo, você pode usar a string "Siga-me na rede". Você pode usar um identificador de recurso da string nesse rótulo. viewContactNotifyService
- O nome de classe totalmente qualificado de um serviço em seu aplicativo que deve receber notificações quando o usuário visualiza um contato. Essa notificação é enviada pelo aplicativo de contatos permite que o aplicativo adie operações com uso intensivo de dados até que sejam necessários. Por exemplo, o aplicativo pode responder a essa notificação lendo e exibindo a foto em alta resolução do contato e a foto mais recente itens de fluxos de redes sociais. Esse recurso é descrito com mais detalhes na seção Interações de fluxos sociais.
viewGroupActivity
- O nome de classe totalmente qualificado de uma atividade no aplicativo que pode exibir informações do grupo. Quando o usuário clica no rótulo do grupo no aplicativo de contatos do dispositivo, a interface dessa atividade é exibida.
viewGroupActionLabel
-
O rótulo que o aplicativo de contatos exibe para um controle de interface que permite
ao usuário conferir grupos no seu aplicativo.
Um identificador de recurso de string é permitido para esse atributo.
viewStreamItemActivity
- O nome de classe totalmente qualificado de uma atividade no aplicativo que o aplicativo de contatos do dispositivo inicia quando o usuário clica em um item de fluxo de um contato bruto.
viewStreamItemPhotoActivity
- O nome de classe totalmente qualificado de uma atividade no seu aplicativo que o O aplicativo de contatos é iniciado quando o usuário clica em uma foto no item de fluxo. para um contato bruto.
<ContactsDataKind> elemento
O elemento <ContactsDataKind>
controla a exibição das linhas de dados
personalizados do aplicativo na interface do aplicativo de contatos. Ele tem a seguinte sintaxe:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
contidas em:
<ContactsAccountType>
Description:
Use este elemento para que o aplicativo de contatos exiba o conteúdo de uma linha de dados personalizada como
os detalhes de um contato bruto. Cada elemento filho <ContactsDataKind>
de <ContactsAccountType>
representa um tipo de linha de dados personalizados que sua sincronização
é adicionado à tabela ContactsContract.Data
. Adicionar um
<ContactsDataKind>
para cada tipo MIME personalizado usado. Não é necessário
adicionar o elemento se você tiver uma linha de dados personalizados em que não quer exibir dados.
Atributos:
android:mimeType
-
O tipo MIME personalizado definido para um dos tipos de linha de dados personalizados na
tabela
ContactsContract.Data
. Por exemplo, o valorvnd.android.cursor.item/vnd.example.locationstatus
pode ser um valor personalizado Tipo MIME para uma linha de dados que registra a última localização conhecida de um contato. android:icon
- Um Android recurso drawable que o aplicativo de contatos exibe ao lado dos seus dados. Use isso para indicar de que os dados são provenientes do seu serviço.
android:summaryColumn
- O nome da coluna para o primeiro dos dois valores recuperados da linha de dados. O é exibido como a primeira linha da entrada para essa linha de dados. A primeira linha se destina ao uso como um resumo dos dados, mas isso é opcional. Consulte também android:detailColumn.
android:detailColumn
-
É o nome da coluna do segundo de dois valores recuperados da linha de dados. O valor é
exibido como a segunda linha da entrada para essa linha de dados. Consulte também
android:summaryColumn
:
Recursos adicionais do Provedor de contatos
Além dos principais recursos descritos nas seções anteriores, o Provedor de contatos oferece estes recursos úteis para trabalhar com dados de contatos:
- Grupos de contatos
- Recursos de foto
Grupos de contatos
O Provedor de contatos pode, opcionalmente, marcar conjuntos de contatos relacionados com
agrupar dados. Se o servidor associado a uma conta de usuário
quiser manter grupos, o adaptador de sincronização para o tipo da conta vai precisar transferir
dados de grupo entre o Provedor de Contatos e o servidor. Quando os usuários adicionam um novo contato ao
e, em seguida, colocar esse contato em um novo grupo, o adaptador de sincronização precisará adicionar o novo grupo
à tabela ContactsContract.Groups
. O grupo ou os grupos de dados brutos
ao qual contato pertencem são armazenados na tabela ContactsContract.Data
, usando
o tipo MIME ContactsContract.CommonDataKinds.GroupMembership
.
Se você estiver projetando um adaptador de sincronização que vai adicionar dados de contato bruto do servidor ao Provedor de contatos e não estiver usando grupos, será necessário fazer com que o provedor torne os dados visíveis. No código executado quando um usuário adiciona uma conta
ao dispositivo, atualize o ContactsContract.Settings
que o Provedor de contatos adiciona à conta. Nessa linha, defina o valor do atributo
Settings.UNGROUPED_VISIBLE
como 1. Quando você fizer isso, o Provedor de contatos sempre
tornar seus dados de contatos visíveis, mesmo que você não use grupos.
Fotos de contatos
A tabela ContactsContract.Data
armazena fotos como linhas com tipo MIME
Photo.CONTENT_ITEM_TYPE
. A coluna
CONTACT_ID
da linha é vinculada à
coluna _ID
do contato bruto a que pertence.
A classe ContactsContract.Contacts.Photo
define uma subtabela de
ContactsContract.Contacts
contendo informações da foto de um contato
foto principal, que é a foto principal do contato bruto principal do contato. Da mesma forma,
a classe ContactsContract.RawContacts.DisplayPhoto
define uma subtabela
ContactsContract.RawContacts
contendo informações de foto de uma
foto principal do contato bruto.
A documentação de referência de ContactsContract.Contacts.Photo
e
ContactsContract.RawContacts.DisplayPhoto
contém exemplos de
como recuperar informações de fotos. Não há classe de conveniência para recuperar a instância principal
miniatura para um contato bruto, mas pode enviar uma consulta ao
ContactsContract.Data
, selecionando a tabela
_ID
, os
Photo.CONTENT_ITEM_TYPE
e IS_PRIMARY
para encontrar a linha da foto principal do contato bruto.
Dados de streams sociais de uma pessoa também podem conter fotos. Elas são armazenadas na tabela android.provider.ContactsContract.StreamItemPhotos, descrita em mais detalhes na seção Fotos de fluxos sociais.