O Provedor de contatos é um componente do Android eficiente e flexível 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. Também é possível acessar os dados dele no seu próprio aplicativo e transferir dados entre o dispositivo e os serviços on-line. O provedor acomoda uma ampla variedade de fontes de dados e tenta gerenciar o máximo de dados possível para cada pessoa, o que resulta em uma organização complexa. Por isso, a API do provedor inclui um conjunto extenso de classes e interfaces de contrato que facilitam a recuperação e a modificação de dados.
Neste guia, descrevemos o seguinte:
- A estrutura básica do provedor.
- Como recuperar dados do provedor.
- Como modificar dados no provedor.
- Como criar um adaptador de sincronização para sincronizar dados do seu servidor com o Provedor de contatos.
Este guia pressupõe que você conhece os conceitos básicos dos provedores de conteúdo do Android. Para saber mais sobre os provedores de conteúdo do Android, leia o guia Noções básicas sobre provedores de conteúdo.
Organização do provedor de contatos
O Provedor de Contatos é um componente de provedor de conteúdo do Android. Ele mantém três tipos de dados sobre uma pessoa, cada um correspondendo a uma tabela oferecida pelo provedor, conforme ilustrado na Figura 1:
Figura 1. Estrutura da tabela do Provedor de contatos.
As três tabelas são geralmente chamadas pelos nomes das classes de contrato. As classes definem constantes para URIs de conteúdo, nomes de colunas e valores de colunas usados pelas tabelas:
-
ContactsContract.Contactstabela - Linhas que representam pessoas diferentes, com base em agregações de linhas de contato brutas.
-
ContactsContract.RawContactstabela - Linhas que contêm um resumo dos dados de uma pessoa, específicos para uma conta e um tipo de usuário.
-
ContactsContract.Datatabela - Linhas que contêm 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 as operações de TI ou oferecer suporte
a funções específicas nos apps de contatos ou de telefonia do dispositivo.
Contatos brutos
Um contato bruto representa os dados de uma pessoa provenientes de um único tipo e nome de conta. Como o Provedor de contatos permite mais de um serviço on-line como fonte de dados para uma pessoa, ele permite vários contatos brutos para a mesma pessoa. Vários contatos brutos também permitem que um usuário combine os dados de uma pessoa de mais de uma conta do mesmo tipo de conta.
A maioria dos dados de um contato bruto não é armazenada na
tabela ContactsContract.RawContacts. Em vez disso, ele é armazenado em uma ou mais
linhas na tabela ContactsContract.Data. Cada linha de dados tem uma coluna
Data.RAW_CONTACT_ID que
contém o valor RawContacts._ID da
linha ContactsContract.RawContacts pai.
Colunas importantes de contato bruto
As colunas importantes na tabela ContactsContract.RawContacts estão
listadas na tabela 1. Leia as observações que seguem a tabela:
Tabela 1. Colunas importantes de contatos brutos.
| Nome da coluna | Usar | Observações |
|---|---|---|
ACCOUNT_NAME
|
O nome da conta para o tipo de conta que é a origem desse contato bruto.
Por exemplo, o nome de uma Conta do Google é um dos endereços do Gmail do proprietário do
dispositivo. Consulte a próxima entrada para
ACCOUNT_TYPE para mais
informações.
|
O formato desse nome é específico do tipo de conta. Não é necessariamente um endereço de e-mail. |
ACCOUNT_TYPE
|
O tipo de conta que é a origem desse contato bruto. Por exemplo, o tipo de uma Conta do Google é com.google. Sempre qualifique seu tipo de conta
com um identificador de um domínio que você possui ou controla. Isso garante que o tipo de conta seja exclusivo.
|
Um tipo de conta que oferece dados de contatos geralmente tem um adaptador de sincronização associado que é sincronizado com o Provedor de contatos. |
DELETED
|
A sinalização "excluído" de um contato bruto. | Essa sinalização permite que o Provedor de contatos mantenha a linha internamente até que os adaptadores de sincronização possam excluí-la dos servidores e, por fim, do repositório. |
Observações
Veja a seguir observações importantes sobre a
tabela ContactsContract.RawContacts:
-
O nome de um contato bruto não é armazenado na linha dele 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 conta em uma linha de contato bruto, eles precisam
ser registrados primeiro com o
AccountManager. Para fazer isso, peça aos usuários que adicionem o tipo de conta e o nome da conta à lista de contas. Se você não fizer isso, o Provedor de contatos vai excluir automaticamente a linha de contato bruto.Por exemplo, se você quiser que o app mantenha os dados de contatos do seu serviço baseado na Web com o domínio
com.example.dataservice, e a conta do usuário para seu serviço forbecky.sharp@dataservice.example.com, o usuário precisará 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 brutas. Você pode explicar esse requisito ao usuário na documentação ou solicitar que ele adicione o tipo e o nome, ou ambos. Os tipos e nomes de conta são descritos em mais detalhes na próxima seção.
Origens de dados de contato bruto
Para entender como os contatos brutos funcionam, considere a usuária "Emily Dickinson", que tem as seguintes três contas de usuário definidas no dispositivo:
emily.dickinson@gmail.comemilyd@gmail.com- Conta do Twitter "belle_of_amherst"
Esse usuário ativou a opção Sincronizar contatos para as três contas nas configurações de Contas.
Suponha que Emily Dickinson abra uma janela do navegador, faça login no Gmail como
emily.dickinson@gmail.com, abra
os Contatos e adicione "Thomas Higginson". Depois, ela faz login no Gmail como
emilyd@gmail.com e envia um e-mail para "Thomas Higginson", que é adicionado
automaticamente como um contato. Ela também segue "colonel_tom" (ID do Twitter de Thomas Higginson) no
Twitter.
O Provedor de contatos cria três contatos brutos como resultado desse trabalho:
-
Um contato bruto para "Thomas Higginson" associado a
emily.dickinson@gmail.com. O tipo de conta de usuário é do Google. -
Um segundo contato bruto para "Thomas Higginson" associado a
emilyd@gmail.com. O tipo de conta de usuário também é do Google. Há um segundo contato bruto, mesmo que o nome seja idêntico ao anterior, porque a pessoa foi adicionada para uma conta de usuário diferente. - Um terceiro contato bruto para "Thomas Higginson" associado a "belle_of_amherst". O tipo de conta de usuário é Twitter.
Dados
Como observado anteriormente, os dados de um contato bruto são armazenados em uma
linha ContactsContract.Data vinculada ao valor
_ID do contato bruto. Isso permite que um único contato bruto tenha várias 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
associada à Conta do Google emilyd@gmail.com) tiver um endereço de e-mail residencial
thigg@gmail.com e um endereço de e-mail do trabalho
thomas.higginson@gmail.com, o Provedor de contatos vai armazenar as duas linhas de endereço de e-mail
e vincular as duas ao contato bruto.
Observe que diferentes tipos de dados são armazenados nessa única tabela. As linhas de detalhes de nome de exibição,
número de telefone, e-mail, endereço postal, foto e site são encontradas na
tabela ContactsContract.Data. Para ajudar a gerenciar isso, a
tabela ContactsContract.Data tem algumas colunas com nomes descritivos
e outras com nomes genéricos. O conteúdo de uma coluna de nome descritivo tem o mesmo significado,
independentemente do tipo de dados na linha, enquanto o conteúdo de uma coluna de nome genérico tem
significados diferentes dependendo do tipo de dados.
Nomes de colunas descritivos
Alguns exemplos de nomes de coluna descritivos:
-
RAW_CONTACT_ID -
O valor da coluna
_IDdo 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 podem ser usados 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 para um contato bruto, a coluna
IS_PRIMARYsinalizará a linha de dados que contém os dados principais do tipo. Por exemplo, se o usuário tocar e manter pressionado o número de telefone de um contato e selecionar Definir padrão, a linhaContactsContract.Dataque contém esse número terá a colunaIS_PRIMARYdefinida como um valor diferente de zero.
Nomes de colunas genéricos
Há 15 colunas genéricas chamadas de DATA1 a
DATA15 que estão em disponibilidade geral e mais quatro colunas genéricas,
de SYNC1 a SYNC4, que só podem ser usadas por adaptadores de
sincronização. As constantes de nome de coluna genéricas sempre funcionam, independentemente do tipo de
dados que a linha contém.
A coluna DATA1 é indexada. O Provedor de contatos sempre usa essa coluna para
os dados que o provedor espera que sejam o destino mais frequente 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 grande objeto binário
(BLOB), como miniaturas de fotos.
Nomes de colunas específicos do tipo
Para facilitar o trabalho com as colunas de um tipo específico de linha, o Provedor de contatos
também fornece constantes de nome de coluna específicas do tipo, definidas em subclasses de
ContactsContract.CommonDataKinds. As constantes simplesmente dão um
nome diferente ao mesmo nome de coluna, o que ajuda a acessar dados em uma linha de um
tipo específico.
Por exemplo, a classe ContactsContract.CommonDataKinds.Email define
constantes de nome de coluna específicas do tipo para uma linha ContactsContract.Data
que tem o tipo MIME
Email.CONTENT_ITEM_TYPE. A classe contém a constante
ADDRESS para a coluna de endereço
de e-mail. O valor real de ADDRESS é "data1", que é o mesmo que o nome genérico da coluna.
Atenção:não adicione dados personalizados à
tabela ContactsContract.Data usando uma linha que tenha um dos
tipos MIME predefinidos do provedor. Se você fizer isso, poderá perder os dados ou causar o mau funcionamento do
provedor. Por exemplo, não adicione uma linha com o tipo MIME
Email.CONTENT_ITEM_TYPE que contenha um nome de usuário em vez de um endereço de e-mail na
coluna DATA1. Se você usar seu próprio tipo MIME personalizado para a linha, poderá
definir seus próprios nomes de coluna específicos do tipo e usar as colunas como quiser.
A Figura 2 mostra como as colunas descritivas e de dados aparecem em uma linha ContactsContract.Data e como os nomes de colunas específicos de tipo "sobrepõem" os nomes de colunas genéricos.
Figura 2. Nomes de colunas específicos de tipo e nomes de colunas genéricos.
Classes de nome de coluna específicas do tipo
A Tabela 2 lista as classes de nome de coluna específicas de tipo mais usadas:
Tabela 2. Classes de nome de coluna específicas do tipo
| Classe de mapeamento | Tipos de dados | Observações |
|---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Os dados de nome do contato bruto associado a essa linha de dados. | Um contato bruto tem apenas uma dessas linhas. |
ContactsContract.CommonDataKinds.Photo |
A foto principal do contato bruto associado a essa linha de dados. | Um contato bruto tem apenas uma dessas linhas. |
ContactsContract.CommonDataKinds.Email |
Um endereço de e-mail para o contato bruto associado a essa linha de dados. | Um contato bruto pode ter vários endereços de e-mail. |
ContactsContract.CommonDataKinds.StructuredPostal |
Um endereço postal para o contato bruto associado a essa linha de dados. | Um contato bruto pode ter vários endereços postais. |
ContactsContract.CommonDataKinds.GroupMembership |
Um identificador que vincula o contato bruto a um dos grupos no Provedor de contatos. | Os grupos são um recurso opcional de um tipo e nome de conta. Eles são descritos em mais detalhes na seção Grupos de contatos. |
Contatos
O Provedor de contatos combina as linhas de contato bruto de todos os tipos e nomes de conta para formar um contato. Isso facilita a exibição e a modificação de todos os dados que um usuário coletou para uma pessoa. O Provedor de contatos gerencia a criação de novas linhas de contato e a agregação de contatos brutos com 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(), vai receber
uma exceção UnsupportedOperationException. Se você tentar atualizar uma coluna
listada 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 corresponde a nenhum contato atual. O provedor também faz isso se os dados de um contato bruto existente mudarem de forma que não correspondam mais ao contato ao qual estavam vinculados anteriormente. Se um aplicativo ou adaptador de sincronização criar um novo contato bruto que corresponda a um contato existente, o novo contato bruto será agregado ao contato existente.
O Provedor de contatos vincula uma linha de contato às linhas de contato bruto com a coluna
_ID da linha de contato na tabela
Contacts. A coluna CONTACT_ID da tabela de contatos brutos
ContactsContract.RawContacts contém valores _ID 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 de contato. Como o Provedor de contatos mantém os contatos
automaticamente, ele pode mudar o valor _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 o
LOOKUP_KEY do contato ainda
vai apontar para a linha do contato. Assim, você pode usar
LOOKUP_KEY
para manter links para contatos "favoritos" e assim por diante. Essa coluna tem o próprio formato, que não está
relacionado ao formato da coluna _ID.
A Figura 3 mostra como as três tabelas principais se relacionam.
Figura 3. Relações entre as tabelas Contacts, contatos brutos e Details.
Atenção : se você publicar o app na Google Play Store ou se ele estiver em um dispositivo com o Android 10 (nível 29 da API) ou mais recente, lembre-se de que um conjunto limitado de campos de dados e métodos de contatos está obsoleto.
Nas condições mencionadas, o sistema limpa periodicamente os valores gravados nestes 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 acima também estão obsoletas:
Além disso, os campos a seguir não retornam mais contatos frequentes. Alguns desses campos influenciam as classificações de contatos apenas quando os contatos fazem parte de um tipo de dados específico.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI -
ContactsContract.Contacts.CONTENT_STREQUENT_URI -
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI -
CONTENT_FILTER_URI(afeta apenas os tipos de dados Email, Phone, Callable e Contactables) -
ENTERPRISE_CONTENT_FILTER_URI(afeta apenas os tipos de dados E-mail, Telefone e Chamável)
Se os apps estiverem acessando ou atualizando esses campos ou APIs, use métodos alternativos. Por exemplo, é possível atender a determinados casos de uso usando provedores de conteúdo particulares ou outros dados armazenados no app ou nos sistemas de back-end.
Para verificar se a funcionalidade do app não é afetada por essa mudança, limpe manualmente esses campos de dados. Para fazer isso, execute o seguinte comando ADB 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 contatos diretamente no dispositivo, mas os dados também fluem para o provedor de contatos
dos serviços da Web por meio de adaptadores de sincronização, que automatizam
a transferência de dados entre o dispositivo e os serviços. 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 da 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 pode oferecer suporte a vários nomes de conta para esse tipo. Os 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 da conta se relacionam com os adaptadores e serviços de sincronização.
- 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, os Contatos do Google são um tipo de conta, identificado
pelo código
google.com. Esse valor corresponde ao tipo de conta usado peloAccountManager. - Nome da conta
- Identifica uma conta ou um login específico para um tipo de conta. As contas do Contatos do Google são iguais às Contas do Google, que têm um endereço de e-mail como nome. Outros serviços podem usar um nome de usuário de uma única palavra ou um ID numérico.
Os tipos de conta não precisam ser exclusivos. Um usuário pode configurar várias contas do app Contatos do Google e fazer o download dos dados delas para o Provedor de contatos. Isso pode acontecer se o usuário tiver um conjunto de contatos pessoais para um nome de conta pessoal e outro para o trabalho. Os nomes de conta geralmente são 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 seu serviço para o Provedor de contatos, escreva seu próprio adaptador de sincronização. Isso é descrito em mais detalhes na seção Adaptadores de sincronização do Provedor de contatos.
A Figura 4 mostra como o Provedor de contatos se encaixa no fluxo de dados sobre pessoas. Na caixa "adaptadores de sincronização", cada adaptador é rotulado pelo tipo de conta.
Figura 4. O fluxo de dados do Provedor de Contatos.
Permissões necessárias
Os aplicativos que querem acessar o Provedor de contatos precisam solicitar as seguintes permissões:
- Acesso de leitura a uma ou mais tabelas
-
READ_CONTACTS, especificado emAndroidManifest.xmlcom 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.xmlcom 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 de usuário e as permissões necessárias são discutidos na seção a seguir, O perfil de usuário.
Lembre-se de que os dados de contatos do usuário são pessoais e sensíveis. Os usuários se preocupam com a privacidade, então não querem que os aplicativos coletem dados sobre eles ou os contatos deles. Se não ficar claro por que você precisa de permissão para acessar os dados de contatos, os usuários podem dar uma classificação baixa ao aplicativo ou simplesmente se recusar a instalá-lo.
O perfil de usuário
A tabela ContactsContract.Contacts tem uma única linha que contém
dados de perfil do usuário do dispositivo. Esses dados descrevem o user do dispositivo, e não
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 do perfil pode ter várias linhas de dados. As constantes para acessar o perfil do usuário estão disponíveis na classe ContactsContract.Profile.
O acesso ao perfil de usuário requer permissões especiais. Além das permissões
READ_CONTACTS e
WRITE_CONTACTS necessárias para ler e gravar, o acesso
ao perfil de 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 o perfil de um usuário deve ser considerado sensível. A permissão android.Manifest.permission#READ_PROFILE permite acessar os dados de identificação pessoal do usuário do dispositivo. Não se esqueça de informar ao usuário por que você precisa de permissões de acesso ao perfil de usuário na descrição do 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. Você também pode usar esse URI de conteúdo como o URI de base para recuperar contatos
brutos ou dados do perfil. Por exemplo, este 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 de usuário, teste a coluna
IS_USER_PROFILE da linha. Essa coluna
será definida como "1" se o contato for o perfil de usuário.
Metadados do provedor de contatos
O Provedor de contatos gerencia dados que monitoram o estado dos dados de contatos no
repositório. Esses metadados sobre o repositório são armazenados em vários lugares, incluindo as
linhas das tabelas Contatos brutos, Data e Contacts, a
tabela ContactsContract.Settings e a
tabela ContactsContract.SyncState. A tabela a seguir mostra o
efeito de cada um desses metadados:
Tabela 3. Metadados no provedor de contatos
| Tabela | Coluna | Valores | Significado |
|---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0": não mudou desde a última sincronização. |
Marca os contatos brutos que foram alterados no dispositivo e precisam ser sincronizados de volta com o
servidor. O valor é definido automaticamente pelo Provedor de contatos quando os apps Android
atualizam uma linha.
Os adaptadores de sincronização que modificam as tabelas de dados ou de contatos brutos precisam sempre anexar a
string |
| "1": alterado desde a última sincronização, precisa ser sincronizado novamente com o servidor. | |||
ContactsContract.RawContacts |
VERSION |
O número da versão desta linha. | O Provedor de contatos incrementa automaticamente esse valor sempre que a linha ou os dados relacionados a ela mudam. |
ContactsContract.Data |
DATA_VERSION |
O número da versão desta linha. | O Provedor de contatos incrementa automaticamente esse valor sempre que a linha de dados é alterada. |
ContactsContract.RawContacts |
SOURCE_ID |
Um valor de string que identifica exclusivamente esse contato bruto para a conta em que ele foi criado. |
Quando um adaptador de sincronização cria um novo contato bruto, essa coluna precisa ser definida como o
ID exclusivo do servidor para o contato bruto. Quando um app Android cria um novo
contato bruto, ele precisa deixar essa coluna vazia. Isso sinaliza ao adaptador
de sincronização que ele precisa criar um novo contato bruto no servidor e receber um
valor para SOURCE_ID.
Em particular, o ID da fonte precisa ser exclusivo para cada tipo de conta e deve ser estável em todas as sincronizações:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0": os contatos desse grupo não podem ficar visíveis nas interfaces de apps Android. | Essa coluna é para compatibilidade com servidores que permitem que um usuário oculte contatos em determinados grupos. |
| "1": os contatos desse grupo podem ficar visíveis nas interfaces do aplicativo. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0": para essa conta e tipo de conta, os contatos que não pertencem a um grupo ficam invisíveis para as interfaces de apps Android. |
Por padrão, os contatos ficam invisíveis se nenhum dos contatos brutos pertencer a um grupo
(a associação de um contato bruto a um grupo é indicada por uma ou mais
linhas ContactsContract.CommonDataKinds.GroupMembership
na tabela ContactsContract.Data).
Ao definir essa sinalização na linha da tabela ContactsContract.Settings
para um tipo de conta e uma conta, é possível forçar a visibilidade de contatos sem grupos.
Um uso dessa flag é mostrar contatos de servidores que não usam grupos.
|
| "1": para essa conta e tipo de conta, os contatos que não pertencem a um grupo ficam visíveis para as interfaces do aplicativo. | |||
ContactsContract.SyncState |
(todos) | Use essa tabela para armazenar metadados do adaptador de sincronização. | Com essa tabela, é possível armazenar o estado de sincronização e outros dados relacionados de forma persistente no dispositivo. |
Acesso ao provedor de contatos
Nesta seção, descrevemos as diretrizes para acessar dados do Provedor de contatos, com foco no seguinte:
- Consultas de entidade.
- Modificação em lote.
- Recuperação e modificação com intents.
- Integridade de dados.
As modificações de um adaptador de sincronização também são abordadas em mais detalhes na seção Adaptadores de sincronização do Provedor de contatos.
Como consultar entidades
Como as tabelas do Provedor de contatos são organizadas em uma hierarquia, geralmente é útil
recuperar uma linha e todas as linhas "filhas" vinculadas a ela. Por exemplo, para mostrar
todas as informações de uma pessoa, é possível recuperar todas as
linhas ContactsContract.RawContacts de uma única linha
ContactsContract.Contacts ou todas as
linhas ContactsContract.CommonDataKinds.Email de uma única linha
ContactsContract.RawContacts. Para facilitar isso, o Provedor de
contatos oferece construções de entidade, que funcionam como mesclagens de banco de dados entre
tabelas.
Uma entidade é como uma tabela composta de colunas selecionadas de uma tabela pai e da tabela filha dela.
Ao consultar uma entidade, você fornece uma projeção e critérios de pesquisa com base nas colunas
disponíveis na 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 um nome de contato
e todas as linhas ContactsContract.CommonDataKinds.Email para todos os
contatos brutos desse nome, você vai receber um Cursor contendo uma linha
para cada linha ContactsContract.CommonDataKinds.Email.
As entidades simplificam as consultas. Usando uma entidade, é possível recuperar todos os dados de um contato ou contato bruto de uma só vez, em vez de consultar primeiro a tabela pai para receber um ID e, em seguida, consultar a tabela filha 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 internamente consistentes.
Observação:uma entidade geralmente não contém todas as colunas da tabela pai e
filho. Se você tentar trabalhar com um nome de coluna que não está na lista de constantes de nome de coluna
da entidade, vai receber um 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: "main" e "detail". A atividade principal
mostra uma lista de linhas de contato. Quando o usuário seleciona uma, a atividade envia o ID dela para a atividade
de detalhes. A atividade de detalhes usa o ContactsContract.Contacts.Entity
para mostrar todas as linhas de dados de todos os contatos brutos associados ao contato
selecionado.
Este snippet é da atividade "detail":
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, é possível receber os
dados desse Cursor para exibi-los ou trabalhar com eles.
Modificação em lote
Sempre que possível, insira, atualize e exclua dados no Provedor de contatos no
"modo em lote", criando um 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, suas modificações nunca vão deixar o repositório de contatos em um estado
inconsistente. Uma modificação em lote também facilita a inserção de um contato bruto e os dados detalhados dele ao
mesmo tempo.
Observação:para modificar um único contato bruto, envie uma intent para o aplicativo de contatos do dispositivo em vez de processar a modificação no seu app. Isso é descrito em mais detalhes na seção Recuperação e modificação com intents.
Pontos de rendimento
Uma modificação em lote com um grande número de operações pode bloquear outros processos,
resultando em uma experiência 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 elas
bloqueiem o sistema, defina 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
permitir que outros processos sejam executados e fecha a transação atual. Quando o provedor é iniciado novamente, ele
continua com a próxima operação no ArrayList e inicia uma nova
transação.
Os pontos de rendimento resultam em mais de uma transação por chamada para
applyBatch(). Por isso, defina um ponto de rendimento para a última operação de um conjunto de linhas relacionadas.
Por exemplo, defina um ponto de rendimento para a última operação em um conjunto que adiciona
linhas de contato bruto e as linhas de dados associadas ou a última operação para 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 serão bem-sucedidos ou falharão como uma única unidade. Se você não definir nenhum ponto de rendimento, a menor operação atômica será o lote inteiro de operações. Se você usar pontos de rendimento, vai impedir que as operações prejudiquem o desempenho do sistema e, ao mesmo tempo, vai garantir que um subconjunto de operações seja atômico.
Referências de retorno de modificação
Ao inserir uma nova linha de contato bruto e as linhas de dados associadas como um conjunto de
objetos ContentProviderOperation, é necessário vincular as linhas de dados à
linha de contato bruto inserindo o valor
_ID do contato bruto como o
valor RAW_CONTACT_ID. No entanto, esse
valor não está disponível quando você cria o ContentProviderOperation
para a linha de dados, porque você ainda não aplicou o
ContentProviderOperation para a linha de contato bruto. Para contornar isso,
a classe ContentProviderOperation.Builder tem o método
withValueBackReference().
Esse método permite inserir ou modificar uma coluna com o
resultado de uma operação anterior.
O método 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 você está modificando.
-
previousResult -
O índice com base em zero de um valor na matriz de
objetos
ContentProviderResultdeapplyBatch(). Conforme as operações em lote são aplicadas, o resultado de cada uma é armazenado em uma matriz intermediária de resultados. O valorpreviousResulté o índice de um desses resultados, que é recuperado e armazenado com o valorkey. Isso permite inserir um novo registro de contato bruto e receber de volta o valor_IDdele. Em seguida, faça uma "referência inversa" ao valor ao adicionar uma linhaContactsContract.Data.A matriz de resultados inteira é criada quando você chama
applyBatch()pela primeira vez, com um tamanho igual ao tamanho doArrayListde objetosContentProviderOperationque você fornece. No entanto, todos os elementos na matriz de resultados são definidos comonull. Se você tentar fazer uma referência inversa a um resultado de uma operação que ainda não foi aplicada,withValueBackReference()vai gerar umException.
Os snippets a seguir mostram como inserir um novo contato bruto e dados em lote. Eles incluem um código que estabelece um ponto de rendimento e usa uma referência de retorno.
O primeiro snippet recupera dados de contato da interface. Nesse ponto, o usuário já selecionou a conta em que o novo contato bruto 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 o nome de exibição, o telefone e o e-mail.
Cada objeto builder de operação usa
withValueBackReference()
para receber o
RAW_CONTACT_ID. A referência aponta
de volta para o 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 é vinculada automaticamente pelo
RAW_CONTACT_ID
à nova linha ContactsContract.RawContacts a que 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 o novo contato bruto e as linhas 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 o controle de simultaneidade otimista, um método de aplicação de transações de modificação sem precisar bloquear o repositório subjacente. Para usar esse método, aplique a transação e verifique se há outras modificações que podem ter sido feitas ao mesmo tempo. Se você encontrar uma modificação inconsistente, reverta a transação e tente novamente.
O controle de simultaneidade otimista é útil para um dispositivo móvel, em que há apenas um usuário por vez e os acessos simultâneos a um repositório de dados são raros. Como o bloqueio não é usado, não há perda de tempo para definir bloqueios ou esperar que outras transações liberem os bloqueios.
Para usar o controle de simultaneidade otimista ao atualizar uma única linha do ContactsContract.RawContacts, siga estas etapas:
-
Recupere a coluna
VERSIONdo contato bruto com os outros dados que você recuperar. -
Crie um objeto
ContentProviderOperation.Builderadequado para aplicar uma restrição usando o métodonewAssertQuery(Uri). Para o URI de conteúdo, useRawContacts.CONTENT_URIcom o_IDdo contato bruto anexado. -
Para o objeto
ContentProviderOperation.Builder, chamewithValue()para comparar a colunaVERSIONcom o 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 objetoContentProviderOperatione adicione-o como o primeiro objeto noArrayListque você transmite paraapplyBatch(). - Aplique a transação em lote.
Se a linha do contato bruto for atualizada por outra operação entre o momento em que você a lê e
o momento em que tenta modificá-la, a ContentProviderOperation de "declaração"
vai falhar, e todo o lote de operações será cancelado. Em seguida, você pode tentar novamente
o lote ou realizar outra ação.
O snippet abaixo demonstra como criar uma "declaração"
ContentProviderOperation depois de 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 uma intent para o aplicativo de contatos do dispositivo permite que você acesse o Provedor de contatos indiretamente. A intent inicia a interface do aplicativo de contatos do dispositivo, em que os usuários podem realizar tarefas relacionadas a contatos. Com esse tipo de acesso, os usuários podem:
- Escolha um contato de uma lista e faça com que ele seja retornado ao app para continuar o trabalho.
- Editar os dados de um contato.
- Inserir um novo contato bruto em qualquer uma das contas.
- Excluir um contato ou dados de 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 criar sua própria interface ou código para acessar o provedor. Você também não precisa solicitar permissão para ler ou gravar no provedor. O aplicativo de contatos do dispositivo pode delegar a permissão de leitura de um contato para você. Como você está fazendo modificações no provedor por outro aplicativo, não é necessário ter permissões de gravação.
O processo geral de envio de uma intent para acessar um provedor é descrito em detalhes no guia
Noções básicas do provedor de conteúdo, na seção "Acesso a dados por 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 os
valores extras que podem ser usados com
putExtra() estão listados na
documentação de referência de ContactsContract.Intents.Insert:
Tabela 4. Intents do provedor de contatos.
| Tarefa | Ação | Dados | Tipo MIME | Observações |
|---|---|---|---|---|
| Escolher um contato em uma lista | ACTION_PICK |
Uma destas opções:
|
Não utilizado |
Mostra uma lista de contatos brutos ou uma lista de dados de um contato bruto, dependendo do
tipo de URI de conteúdo fornecido.
Chame
|
| Inserir um novo contato bruto | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE, tipo MIME para um conjunto de contatos brutos.
|
Mostra a tela Adicionar contato do aplicativo de contatos do dispositivo. Os
valores extras adicionados à intent são mostrados. Se enviado com
startActivityForResult(),
o URI de conteúdo do contato bruto recém-adicionado será transmitido de volta ao método de callback
onActivityResult()
da atividade no argumento Intent, no campo
"data". Para receber o valor, chame getData().
|
| Editar um contato | ACTION_EDIT |
CONTENT_LOOKUP_URI para
o contato. A atividade do editor vai permitir que o usuário edite qualquer um dos dados associados
a esse contato.
|
Contacts.CONTENT_ITEM_TYPE, um único contato. |
Mostra a tela "Editar contato" no aplicativo de contatos. Os valores extras adicionados à intent são exibidos. Quando o usuário clica em Concluído para salvar as edições, a atividade retorna ao primeiro plano. |
| Mostre um seletor que também pode adicionar dados. | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Essa intent sempre mostra a tela de seleção do app Contatos. O usuário pode
escolher um contato para editar ou adicionar um novo. 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 mostrados. 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 atual ou adiciona um novo. Além disso, se você enviar um nome e o usuário optar por fazer uma edição, o app Contatos vai mostrar 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 Contatos do dispositivo não permite excluir um contato bruto ou qualquer um dos dados dele com uma intent. Em vez disso, para excluir um contato bruto, use
ContentResolver.delete()
ou ContentProviderOperation.newDelete().
O snippet a seguir mostra como criar e enviar uma intent que insere um novo contato bruto 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 sensíveis que os usuários esperam que estejam corretos e atualizados, o Provedor de contatos tem regras bem definidas para a integridade de dados. É sua responsabilidade obedecer a essas regras ao modificar dados de contatos. As regras importantes estão listadas aqui:
-
Sempre adicione uma linha
ContactsContract.CommonDataKinds.StructuredNamepara cada linhaContactsContract.RawContactsadicionada. -
Uma linha
ContactsContract.RawContactssem uma linhaContactsContract.CommonDataKinds.StructuredNamena tabelaContactsContract.Datapode causar problemas durante a agregação. -
Sempre vincule novas linhas
ContactsContract.Dataà linhaContactsContract.RawContactsmãe. -
Uma linha
ContactsContract.Dataque não está vinculada a umContactsContract.RawContactsnão vai ficar visível no aplicativo de contatos do dispositivo e pode causar problemas com adaptadores de sincronização. - Mude os dados apenas dos contatos brutos que pertencem a você.
- Lembre-se de que o Provedor de contatos geralmente gerencia dados de vários tipos de conta/serviços on-line diferentes. É preciso garantir que o aplicativo modifique ou exclua apenas dados de linhas que pertencem a você e que insira apenas dados com um tipo de conta e nome que você controla.
-
Sempre use as constantes definidas em
ContactsContracte as subclasses para autoridades, URIs de conteúdo, caminhos de URI, nomes de colunas, tipos MIME e valoresTYPE. - O uso dessas constantes ajuda a evitar erros. Você também vai receber notificações com avisos do compilador se alguma das constantes for descontinuada.
Linhas de dados personalizadas
Ao criar e usar seus próprios tipos MIME personalizados, é possível inserir, editar, excluir e recuperar
suas próprias linhas de dados na tabela ContactsContract.Data. As linhas
são limitadas ao uso da coluna definida em
ContactsContract.DataColumns, mas é possível mapear os próprios
nomes de coluna específicos do tipo para os nomes de coluna padrão. No aplicativo de contatos do dispositivo,
os dados das suas linhas são mostrados, mas não podem ser editados ou excluídos, e os usuários não podem adicionar
outros dados. Para permitir que os usuários modifiquem suas linhas de dados personalizadas, forneça uma atividade de editor
no seu próprio aplicativo.
Para exibir seus dados personalizados, forneça um arquivo contacts.xml que contenha um
elemento <ContactsAccountType> e um ou mais dos
elementos filhos <ContactsDataKind>. Isso é descrito com mais detalhes na
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 processar a sincronização de dados de contatos entre um dispositivo e um serviço on-line. Isso permite que os usuários baixem dados de um dispositivo para outro e façam upload de dados para uma nova conta. A sincronização também garante que os usuários tenham os dados mais recentes em mãos, independentemente da origem das adições e mudanças. Outra vantagem da sincronização é que ela disponibiliza os dados de contatos mesmo quando o dispositivo não está conectado à rede.
Embora seja possível implementar a sincronização de várias maneiras, o sistema Android oferece um framework de sincronização de plug-ins que automatiza as seguintes tarefas:
- Verificando a disponibilidade da rede.
- Programar e executar a sincronização com base nas preferências do usuário.
- Reiniciando as sincronizações que foram interrompidas.
Para usar esse framework, forneça um plug-in de adaptador de sincronização. Cada adaptador de sincronização é exclusivo para um serviço e provedor de conteúdo, mas pode processar vários nomes de conta para o mesmo serviço. O framework também permite vários adaptadores de sincronização para o mesmo serviço e provedor.
Classes e arquivos do adaptador de sincronização
Implemente um adaptador de sincronização como uma subclasse de
AbstractThreadedSyncAdapter e instale-o como parte de um app
Android. O sistema aprende sobre o adaptador de sincronização com elementos no manifesto do aplicativo
e com um arquivo XML especial apontado pelo manifesto. O arquivo XML define o
tipo de conta do serviço on-line e a autoridade do provedor de conteúdo, que juntos
identificam o adaptador de forma exclusiva. O adaptador de sincronização só é ativado quando o usuário adiciona uma
conta para o tipo de conta do adaptador de sincronização e ativa a sincronização para o provedor
de conteúdo com que o adaptador de sincronização é sincronizado. Nesse ponto, o sistema começa a gerenciar o adaptador,
chamando-o conforme necessário para sincronizar o provedor de conteúdo e o servidor.
Observação:o uso de 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 serviços diferentes da
mesma organização. Por exemplo, os adaptadores de sincronização para 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 os Serviços do Google são listados juntos. Cada adaptador de sincronização
listado é sincronizado com um provedor de conteúdo diferente no dispositivo.
Como a maioria dos serviços exige que os usuários verifiquem a identidade antes de acessar
dados, o sistema Android oferece um framework de autenticação semelhante ao framework do adaptador de sincronização,
que geralmente é usado em conjunto com ele. O framework 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 o nome, a senha ou informações semelhantes do usuário (as credenciais do usuário).
- 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 posterior. Devido ao framework do autenticador de plug-in, o
AccountManager pode fornecer acesso a qualquer token de autenticação que um autenticador
ofereça e decida expor, como tokens de autenticação OAuth2.
Embora a autenticação não seja obrigatória, a maioria dos serviços de contatos a utiliza. No entanto, não é necessário usar o framework 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, comece criando um app Android que contenha o seguinte:
-
Um componente
Serviceque responde às solicitações do sistema para vincular ao adaptador de sincronização. -
Quando o sistema quer executar uma sincronização, ele chama o método
onBind()do serviço para receber umIBinderpara 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 real, implementado como uma subclasse concreta de
AbstractThreadedSyncAdapter. -
Essa classe faz o download de dados do servidor, o upload de dados do
dispositivo e a resolução de conflitos. O trabalho principal do adaptador é
feito no método
onPerformSync(). Essa classe precisa ser instanciada como um singleton. -
Uma subclasse de
Application. -
Essa classe funciona como uma fábrica para o singleton do adaptador de sincronização. Use o
método
onCreate()para instanciar o adaptador de sincronização e forneça um método "getter" estático para retornar o singleton ao métodoonBind()do serviço do adaptador de sincronização. -
Opcional:um componente
Serviceque responde às solicitações do sistema para autenticação do usuário. -
O
AccountManagerinicia esse serviço para começar o processo de autenticação. O métodoonCreate()do serviço instancia um objeto autenticador. Quando o sistema quer autenticar uma conta de usuário para o adaptador de sincronização do aplicativo, ele chama o métodoonBind()do serviço para receber umIBinderpara o autenticador. Isso permite que o sistema faça chamadas entre processos para os métodos do autenticador. -
Opcional:uma subclasse concreta de
AbstractAccountAuthenticatorque processa solicitações de autenticação. -
Essa classe fornece métodos que o
AccountManagerinvoca para autenticar as credenciais do usuário com o servidor. Os detalhes do processo de autenticação variam muito, dependendo da tecnologia de servidor em uso. Consulte a documentação do software servidor para saber mais sobre autenticação. - 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
definidos em elementos
<service>no manifesto do aplicativo. Esses elementos contêm elementos filhos<meta-data>que fornecem dados específicos ao sistema:-
O elemento
<meta-data>do serviço do adaptador de sincronização aponta para o arquivo XMLres/xml/syncadapter.xml. Por sua vez, esse arquivo especifica um URI para o serviço da Web que será sincronizado com o Provedor de contatos e um tipo de conta para o serviço da Web. -
Opcional:o elemento
<meta-data>do autenticador aponta para o arquivo XMLres/xml/authenticator.xml. Por sua vez, esse arquivo especifica o tipo de conta que o autenticador aceita, bem como os recursos de interface que aparecem durante o processo de autenticação. O tipo de conta especificado nesse elemento precisa ser o mesmo especificado para o adaptador de sincronização.
-
O elemento
Dados de stream social
As tabelas android.provider.ContactsContract.StreamItems e android.provider.ContactsContract.StreamItemPhotos gerenciam os dados recebidos de redes sociais. Você pode criar um adaptador de sincronização que adiciona dados de stream da sua própria rede a essas tabelas ou pode ler dados de stream dessas tabelas e mostrá-los no seu aplicativo, ou ambos. Com esses recursos, seus serviços e aplicativos de redes sociais podem ser integrados à experiência de redes sociais do Android.
Texto do stream social
Os itens de stream são sempre associados a um contato bruto. O
android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID é vinculado 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 stream.
Armazene os dados do seu stream nas seguintes colunas:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Obrigatório. O tipo de conta do usuário para o contato bruto associado a este item de stream. Defina esse valor ao inserir um item de stream.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Obrigatório. O nome da conta do usuário para o contato bruto associado a este item de stream. Defina esse valor ao inserir um item de stream.
- Colunas de identificador
-
Obrigatório. É necessário inserir as seguintes colunas de identificador ao
inserir um item de stream:
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: o valor android.provider.BaseColumns#_ID do contato ao qual esse item de stream está associado.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: o valor android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY do contato ao qual esse item de stream está associado.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: o valor android.provider.BaseColumns#_ID do contato bruto ao qual esse item de stream está associado.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Opcional. Armazena informações de resumo que podem ser mostradas no início de um item do stream.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
O texto do item do stream, que pode ser o conteúdo postado pela fonte do item
ou uma descrição de alguma ação que gerou o item do stream. Essa coluna pode conter
qualquer formatação e imagens de recursos incorporadas que possam ser renderizadas pelo
fromHtml(). O provedor pode truncar ou colocar reticências em conteúdo longo, mas tentará evitar a quebra de tags. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- Uma string de texto que contém o horário em que o item do stream foi inserido ou atualizado, na forma de milissegundos desde a época. Os aplicativos que inserem ou atualizam itens de stream são responsáveis por manter essa coluna. Ela não é mantida automaticamente pelo Provedor de contatos.
Para exibir 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 android.provider.ContactsContract.StreamItemsColumns#SYNC1 a android.provider.ContactsContract.StreamItemsColumns#SYNC4 para uso exclusivo de adaptadores de sincronização.
Fotos do stream social
A tabela android.provider.ContactsContract.StreamItemPhotos armazena fotos associadas
a um item de stream. A coluna
android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID da tabela
está vinculada aos valores na coluna _ID da tabela
android.provider.ContactsContract.StreamItems. As referências de fotos são armazenadas na
tabela nestas colunas:
- Coluna android.provider.ContactsContract.StreamItemPhotos#PHOTO (um BLOB).
- Uma representação binária da foto, redimensionada pelo provedor para armazenamento e exibição. Essa coluna está disponível para compatibilidade 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 descritos nos pontos a seguir) para armazenar fotos em um arquivo. Essa coluna agora contém uma miniatura da foto, que está disponível para leitura.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Um identificador numérico de uma foto para um contato bruto. Anexe esse valor à constante
DisplayPhoto.CONTENT_URIpara receber um URI de conteúdo que aponta para um único arquivo de foto e, em seguida, chameopenAssetFileDescriptor()para receber um identificador para o arquivo de foto. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
Um URI de conteúdo que aponta diretamente para o arquivo da foto representada por essa linha.
Chame
openAssetFileDescriptor()com esse URI para receber um identificador para o arquivo de foto.
Como usar as tabelas de stream social
Essas tabelas funcionam da mesma forma que as outras tabelas principais no Provedor de contatos, exceto que:
- Essas tabelas exigem permissões de acesso adicionais. Para ler esses dados, o aplicativo precisa ter a permissão android.Manifest.permission#READ_SOCIAL_STREAM. Para modificá-los, 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 é limitado. Quando esse limite é atingido,
o Provedor de contatos abre espaço para novas linhas de itens de stream excluindo automaticamente
as linhas com o
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP mais antigo. Para receber o
limite, emita uma consulta para o 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 que contém uma única linha, com a única coluna android.provider.ContactsContract.StreamItems#MAX_ITEMS.
A classe android.provider.ContactsContract.StreamItems.StreamItemPhotos define uma subtabela de android.provider.ContactsContract.StreamItemPhotos que contém as linhas de foto de um único item de stream.
Interações de stream social
Os dados de fluxo social gerenciados pelo Provedor de contatos, em conjunto com o aplicativo de contatos do dispositivo, oferecem uma maneira eficiente de conectar seu sistema de rede social aos contatos existentes. Os recursos a seguir estão disponíveis:
- Ao sincronizar seu serviço de rede social com o Provedor de contatos usando um adaptador de sincronização, você pode recuperar a atividade recente dos contatos de um usuário e armazená-la nas tabelas android.provider.ContactsContract.StreamItems e android.provider.ContactsContract.StreamItemPhotos para uso posterior.
- Além da sincronização regular, é possível acionar o adaptador de sincronização para recuperar dados adicionais quando o usuário seleciona um contato para visualização. Isso permite que o adaptador de sincronização recupere fotos em 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, você pode receber uma intent quando um contato é visualizado e, nesse momento, atualizar o status do contato no seu serviço. Essa abordagem pode ser mais rápida e usar menos largura de banda do que 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 visualizam o contato no aplicativo de contatos do dispositivo. Isso é feito com o recurso "convidar contato", que é ativado com uma combinação de uma atividade que adiciona um contato existente à sua rede e um arquivo XML que fornece o aplicativo de contatos do dispositivo e 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. O registro de notificações e o convite de contatos são abordados nas próximas duas seções.
Como fazer o registro para processar visualizações de redes sociais
Para registrar o adaptador de sincronização para receber notificações quando o usuário visualizar um contato gerenciado por ele:
-
Crie um arquivo chamado
contacts.xmlno 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 é notificado quando o usuário abre 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 vai receber a intent do aplicativo de contatos do dispositivo. Para o serviço de notificação, use uma classe que estendaIntentServicepara permitir que o serviço receba intents. Os dados na intent recebida contêm o URI de conteúdo do contato bruto 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 clicar em um item de stream, em uma foto ou em ambos:
-
Crie um arquivo chamado
contacts.xmlno 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 uma das suas atividades para processar o clique do usuário 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 vai receber a intent do aplicativo de contatos do dispositivo. -
Para registrar uma das suas atividades para processar o clique do usuário 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 vai receber a intent do aplicativo de contatos do dispositivo.
O elemento <ContactsAccountType> é descrito em mais detalhes na
seção Elemento<ContactsAccountType>.
A intent recebida contém o URI de conteúdo do item ou da foto em que o usuário clicou. Para ter atividades separadas para itens de texto e fotos, use os dois atributos no mesmo arquivo.
Interagir com seu serviço de rede social
Os usuários não precisam sair do aplicativo de contatos do dispositivo para convidar um contato para seu site de rede social. Em vez disso, você pode fazer com que o app Contatos do dispositivo envie uma intent para convidar o contato para uma das suas atividades. Para definir essa configuração:
-
Crie um arquivo chamado
contacts.xmlno 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 vai receber a intent. O valorinvite_action_labelé uma string de texto mostrada no menu Adicionar conexão no aplicativo de contatos do dispositivo.
Observação:ContactsSource é um nome de tag descontinuado para
ContactsAccountType.
Referência de 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.
Elemento <ContactsAccountType>
O elemento <ContactsAccountType> controla a interação do seu
aplicativo 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>
Descrição:
Declara componentes do Android e rótulos de interface que permitem que os usuários convidem um dos contatos para uma rede social, notifiquem os usuários quando um dos streams de rede social for atualizado e assim por diante.
O prefixo de atributo android: não é necessário para os atributos
de <ContactsAccountType>.
Atributos:
inviteContactActivity- O nome totalmente qualificado da classe da atividade no aplicativo que você quer ativar quando o usuário selecionar Adicionar conexão no aplicativo de contatos do dispositivo.
inviteContactActionLabel-
Uma string de texto exibida para a atividade especificada em
inviteContactActivity, no menu Adicionar conexão. Por exemplo, você pode usar a string "Seguir na minha rede". É possível usar um identificador de recurso de string para esse rótulo. viewContactNotifyService- O nome de classe totalmente qualificado de um serviço no seu aplicativo que deve receber notificações quando o usuário visualizar um contato. Essa notificação é enviada pelo aplicativo de contatos do dispositivo e permite que seu aplicativo adie operações com uso intensivo de dados até que sejam necessárias. Por exemplo, seu aplicativo pode responder a essa notificação lendo e exibindo a foto em alta resolução do contato e os itens mais recentes do stream social. Esse recurso é descrito em mais detalhes na seção Interações de stream social.
viewGroupActivity- O nome de classe totalmente qualificado de uma atividade no aplicativo que pode mostrar informações do grupo. Quando o usuário clica no marcador de grupo no aplicativo de contatos do dispositivo, a interface dessa atividade é mostrada.
viewGroupActionLabel-
O rótulo que o aplicativo de contatos mostra para um controle de interface que permite
que o usuário veja 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 seu aplicativo que o aplicativo de contatos do dispositivo inicia quando o usuário clica em um item de stream para um contato bruto.
viewStreamItemPhotoActivity- O nome de classe totalmente qualificado de uma atividade no seu aplicativo que o aplicativo de contatos do dispositivo inicia quando o usuário clica em uma foto no item de stream de um contato bruto.
Elemento <ContactsDataKind>
O elemento <ContactsDataKind> controla a exibição das linhas de dados personalizados
do seu 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">
contido em:
<ContactsAccountType>
Descrição:
Use esse elemento para que o aplicativo de contatos mostre o conteúdo de uma linha de dados personalizados como
parte dos detalhes de um contato bruto. Cada elemento filho <ContactsDataKind>
de <ContactsAccountType> representa um tipo de linha de dados do cliente que o adaptador
de sincronização adiciona à tabela ContactsContract.Data. Adicione um
elemento <ContactsDataKind> para cada tipo MIME personalizado que você usar. Não é necessário
adicionar o elemento se você tiver uma linha de dados personalizada para a qual não quer exibir dados.
Atributos:
android:mimeType-
O tipo MIME personalizado que você definiu para um dos tipos de linha de dados personalizados na
tabela
ContactsContract.Data. Por exemplo, o valorvnd.android.cursor.item/vnd.example.locationstatuspode ser um tipo MIME personalizado para uma linha de dados que registra o último local conhecido de um contato. android:icon- Um recurso drawable do Android que o aplicativo de contatos mostra ao lado dos seus dados. Use isso para indicar ao usuário que os dados vêm do seu serviço.
android:summaryColumn- O nome da coluna para o primeiro de dois valores recuperados da linha de dados. O valor é exibido como a primeira linha da entrada para essa linha de dados. A primeira linha é destinada a ser usada como um resumo dos dados, mas isso é opcional. Consulte também android:detailColumn.
android:detailColumn-
O nome da coluna para o segundo dos 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.
Outros recursos 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 contato
- Recursos de foto
Grupos de contato
O Provedor de contatos pode, opcionalmente, rotular coleções de contatos relacionados com
dados de grupo. Se o servidor associado a uma conta de usuário
quiser manter grupos, o adaptador de sincronização do tipo de conta precisará transferir
dados de grupos entre o Provedor de contatos e o servidor. Quando os usuários adicionam um novo contato ao
servidor e o colocam em um novo grupo, o adaptador de sincronização precisa adicionar o novo grupo
à tabela ContactsContract.Groups. Os grupos a que um contato
bruto pertence são armazenados na tabela ContactsContract.Data, usando
o tipo MIME ContactsContract.CommonDataKinds.GroupMembership.
Se você estiver projetando um adaptador de sincronização que adicionará dados de contato brutos do
servidor ao Provedor de contatos e não estiver usando grupos, será necessário informar ao
provedor para tornar seus dados visíveis. No código executado quando um usuário adiciona uma conta
ao dispositivo, atualize a linha ContactsContract.Settings
que o Provedor de contatos adiciona à conta. Nessa linha, defina o valor da coluna
Settings.UNGROUPED_VISIBLE como 1. Ao fazer isso, o Provedor de contatos sempre
vai tornar os dados dos seus contatos visíveis, mesmo que você não use grupos.
Fotos do contato
A tabela ContactsContract.Data armazena fotos como linhas com o tipo MIME
Photo.CONTENT_ITEM_TYPE. A coluna
CONTACT_ID da linha está vinculada à coluna
_ID do contato bruto a que ela pertence.
A classe ContactsContract.Contacts.Photo define uma subtabela de
ContactsContract.Contacts que contém informações sobre a foto
principal de um contato, que é a foto principal do contato bruto principal. Da mesma forma,
a classe ContactsContract.RawContacts.DisplayPhoto define uma subtabela
de ContactsContract.RawContacts que contém informações sobre a
foto principal de um contato bruto.
A documentação de referência de ContactsContract.Contacts.Photo e
ContactsContract.RawContacts.DisplayPhoto contém exemplos de
recuperação de informações de fotos. Não há uma classe de conveniência para recuperar a miniatura
principal de um contato bruto, mas você pode enviar uma consulta para a
tabela ContactsContract.Data, selecionando o _ID
do contato bruto, o
Photo.CONTENT_ITEM_TYPE e a coluna IS_PRIMARY
para encontrar a linha da foto principal do contato bruto.
Os dados de stream social de uma pessoa também podem incluir fotos. Elas são armazenadas na tabela android.provider.ContactsContract.StreamItemPhotos, que é descrita em mais detalhes na seção Fotos do stream social.