Recuperar detalhes de um contato

Esta lição mostra como recuperar dados detalhados de um contato, como endereços de e-mail, números de telefone e assim por diante. Esses são os detalhes que os usuários procuram quando recuperam um contato. Você pode fornecer todos os detalhes de um contato ou mostrar apenas detalhes de um tipo específico, como endereços de e-mail.

As etapas desta lição pressupõem que você já tenha uma ContactsContract.Contacts linha para um contato escolhido pelo usuário. A lição Recuperar nomes de contatos mostra como recuperar uma lista de contatos.

Recuperar todos os detalhes de um contato

Para recuperar todos os detalhes de um contato, pesquise na ContactsContract.Data tabela linhas que contenham a LOOKUP_KEY do contato. Essa coluna está disponível em a tabela ContactsContract.Data, porque o provedor de contatos faz uma mesclagem implícita entre a tabela ContactsContract.Contacts e a tabela ContactsContract.Data. A coluna LOOKUP_KEY é descrita com mais detalhes na lição Recuperar nomes de contatos.

Observação: a recuperação de todos os detalhes de um contato reduz o desempenho de um dispositivo, porque ele precisa recuperar todas as colunas na ContactsContract.Data tabela. Considere o impacto no desempenho antes de usar essa técnica.

Solicitar permissões

Para ler do provedor de contatos, seu app precisa ter a permissão READ_CONTACTS. Para solicitar essa permissão, adicione o seguinte elemento filho de <manifest> ao arquivo de manifesto:

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

Configurar uma projeção

Dependendo do tipo de dados que uma linha contém, ela só pode usar algumas colunas ou muitas delas. Além disso, os dados estão em colunas diferentes, dependendo do tipo de dados. Para garantir que você receba todas as colunas possíveis para todos os tipos de dados possíveis, adicione todos os nomes de colunas à projeção. Sempre recupere Data._ID se você estiver vinculando o resultado Cursor a um ListView. Caso contrário, a vinculação não vai funcionar. Recupere também Data.MIMETYPE para identificar o tipo de dados de cada linha recuperada. Exemplo:

Kotlin

private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Data._ID,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.DATA2,
        ContactsContract.Data.DATA3,
        ContactsContract.Data.DATA4,
        ContactsContract.Data.DATA5,
        ContactsContract.Data.DATA6,
        ContactsContract.Data.DATA7,
        ContactsContract.Data.DATA8,
        ContactsContract.Data.DATA9,
        ContactsContract.Data.DATA10,
        ContactsContract.Data.DATA11,
        ContactsContract.Data.DATA12,
        ContactsContract.Data.DATA13,
        ContactsContract.Data.DATA14,
        ContactsContract.Data.DATA15
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.Data._ID,
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.DATA1,
                ContactsContract.Data.DATA2,
                ContactsContract.Data.DATA3,
                ContactsContract.Data.DATA4,
                ContactsContract.Data.DATA5,
                ContactsContract.Data.DATA6,
                ContactsContract.Data.DATA7,
                ContactsContract.Data.DATA8,
                ContactsContract.Data.DATA9,
                ContactsContract.Data.DATA10,
                ContactsContract.Data.DATA11,
                ContactsContract.Data.DATA12,
                ContactsContract.Data.DATA13,
                ContactsContract.Data.DATA14,
                ContactsContract.Data.DATA15
            };

Essa projeção recupera todas as colunas de uma linha na tabela ContactsContract.Data, usando os nomes de colunas definidos na classe ContactsContract.Data.

Opcionalmente, você também pode usar outras constantes de coluna definidas ou herdadas pela ContactsContract.Data classe. No entanto, as colunas SYNC1 a SYNC4 são destinadas a adaptadores de sincronização, portanto, os dados delas não são úteis.

Definir os critérios de seleção

Defina uma constante para a cláusula de seleção, uma matriz para manter argumentos de seleção e uma variável para manter o valor de seleção. Use a coluna Contacts.LOOKUP_KEY para encontrar o contato. Exemplo:

Kotlin

// Defines the selection clause
private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")
/*
 * Defines a variable to contain the selection value. Once you
 * have the Cursor from the Contacts table, and you've selected
 * the desired row, move the row's LOOKUP_KEY value into this
 * variable.
 */
private var lookupKey: String? = null

Java

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };
    /*
     * Defines a variable to contain the selection value. Once you
     * have the Cursor from the Contacts table, and you've selected
     * the desired row, move the row's LOOKUP_KEY value into this
     * variable.
     */
    private lateinit var lookupKey: String

O uso de "?" como marcador de posição na expressão de texto de seleção garante que a pesquisa resultante seja gerada por vinculação em vez de compilação de SQL. Essa abordagem elimina a possibilidade de injeção de SQL maliciosa.

Definir a ordem de classificação

Defina a ordem de classificação desejada no Cursor resultante. Para manter todas as linhas de um tipo de dados específico juntas, classifique por Data.MIMETYPE. Esse argumento de consulta agrupa todas as linhas de e-mail, todas as linhas de telefone e assim por diante. Exemplo:

Kotlin

/*
 * Defines a string that specifies a sort order of MIME type
 */
private const val SORT_ORDER = ContactsContract.Data.MIMETYPE

Java

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;

Observação:alguns tipos de dados não usam um subtipo; portanto, não é possível classificá-los por subtipo. Em vez disso, é necessário iterar pelo Cursor, determinar o tipo de dados da linha atual e armazenar dados para linhas que usam um subtipo. Quando terminar de ler o cursor, você poderá classificar cada tipo de dados por subtipo e mostrar os resultados.

Inicializar o carregador

Sempre faça recuperações do provedor de contatos (e de todos os outros provedores de conteúdo) em uma linha de execução em segundo plano. Use o framework do carregador definido pela LoaderManager classe e a LoaderManager.LoaderCallbacks interface para fazer recuperações em segundo plano.

Quando estiver tudo pronto para recuperar as linhas, inicialize o framework do carregador by chamando initLoader(). Transmita um identificador inteiro para o método. Esse identificador é transmitido para os LoaderManager.LoaderCallbacks métodos. O identificador ajuda você a usar vários carregadores em um app, permitindo diferenciá-los.

O seguinte snippet mostra como inicializar o framework do carregador:

Kotlin

// Defines a constant that identifies the loader
private const val DETAILS_QUERY_ID: Int = 0

class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Initializes the loader framework
        loaderManager.initLoader(DETAILS_QUERY_ID, null, this)

Java

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    static int DETAILS_QUERY_ID = 0;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // Initializes the loader framework
        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);

Implementar onCreateLoader()

Implemente o método onCreateLoader(), que é chamado pelo framework do carregador imediatamente após você chamar initLoader(). Retorne um CursorLoader desse método. Como você está pesquisando a tabela ContactsContract.Data, use a constante Data.CONTENT_URI como o URI de conteúdo. Exemplo:

Kotlin

override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
    // Choose the proper action
    mLoader = when(loaderId) {
        DETAILS_QUERY_ID -> {
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey
            // Starts the query
            activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        SORT_ORDER
                )
            }
        }
        ...
    }
    return mLoader
}

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            ContactsContract.Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            selectionArgs,
                            SORT_ORDER
                    );
    }

Implementar onLoadFinished() e onLoaderReset()

Implemente o onLoadFinished() método. O framework do carregador chama onLoadFinished() quando o provedor de contatos retorna os resultados da consulta. Exemplo:

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        when(loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * Process the resulting Cursor here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                break;
            ...
        }
    }

O método onLoaderReset() é invocado quando o framework do carregador detecta que os dados que oferecem suporte ao resultado Cursor foram alterados. Nesse ponto, remova todas as referências atuais ao Cursor definindo-as como nulas. Caso contrário, o framework do carregador não vai destruir o Cursor antigo, e você terá um vazamento de memória. Exemplo:

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        when (loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
                }
                break;
    }

Recuperar detalhes específicos de um contato

A recuperação de um tipo de dados específico para um contato, como todos os e-mails, segue o mesmo padrão da recuperação de todos os detalhes. Estas são as únicas mudanças que você precisa fazer no código listado em Recuperar todos os detalhes de um contato:

Projeção
Modifique a projeção para recuperar as colunas específicas do tipo de dados. Modifique também a projeção para usar as constantes de nome de coluna definidas na ContactsContract.CommonDataKinds subclasse correspondente ao tipo de dados.
Seleção
Modifique o texto de seleção para pesquisar o MIMETYPE valor específico do tipo de dados.
Ordem de classificação
Como você está selecionando apenas um tipo de detalhe, não agrupe o `Cursor` retornado Cursor por Data.MIMETYPE.

Essas modificações são descritas nas seções a seguir.

Definir uma projeção

Defina as colunas que você quer recuperar, usando as constantes de nome de coluna na subclasse de ContactsContract.CommonDataKinds para o tipo de dados. Se você planeja vincular o Cursor a um ListView, recupere a coluna _ID. Por exemplo, para recuperar dados de e-mail, defina a seguinte projeção:

Kotlin

private val PROJECTION: Array<String> = arrayOf(
        ContactsContract.CommonDataKinds.Email._ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.CommonDataKinds.Email.TYPE,
        ContactsContract.CommonDataKinds.Email.LABEL
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL
            };

Observe que essa projeção usa os nomes de colunas definidos na classe ContactsContract.CommonDataKinds.Email, em vez dos nomes de colunas definidos na classe ContactsContract.Data. O uso de nomes de colunas específicos de e-mail torna o código mais legível.

Na projeção, você também pode usar qualquer uma das outras colunas definidas na ContactsContract.CommonDataKinds subclasse.

Definir critérios de seleção

Defina uma expressão de texto de pesquisa que recupere linhas para a LOOKUP_KEY de um contato específico e o Data.MIMETYPE dos detalhes que você deseja. Coloque o valor MIMETYPE entre aspas simples concatenando um caractere "'" (aspas simples) ao início e ao fim da constante. Caso contrário, o provedor interpreta a constante como um nome de variável em vez de um valor de string. Não é necessário usar um marcador de posição para esse valor, porque você está usando uma constante em vez de um valor fornecido pelo usuário. Exemplo:

Kotlin

/*
 * Defines the selection clause. Search for a lookup key
 * and the Email MIME type
 */
private const val SELECTION =
        "${ContactsContract.Data.LOOKUP_KEY} = ? AND " +
        "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")

Java

    /*
     * Defines the selection clause. Search for a lookup key
     * and the Email MIME type
     */
    private static final String SELECTION =
            Data.LOOKUP_KEY + " = ?" +
            " AND " +
            Data.MIMETYPE + " = " +
            "'" + Email.CONTENT_ITEM_TYPE + "'";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };

Definir uma ordem de classificação

Defina uma ordem de classificação para o Cursor retornado. Como você está recuperando um tipo de dados específico, omita a classificação em MIMETYPE. Em vez disso, se o tipo de dados de detalhes que você está pesquisando incluir um subtipo, classifique-o. Por exemplo, para dados de e-mail, você pode classificar por Email.TYPE:

Kotlin

private const val SORT_ORDER: String = "${Email.TYPE} ASC"

Java

    private static final String SORT_ORDER = Email.TYPE + " ASC ";