Como as colunas unidas implícitas funcionam com dados de contatos Android?

Eu estou consultando a tabela ContactsContract.Data paira encontrair registros do telefone.

Recebo um erro ao criair um novo CursorLoader :

  • android getChildView não está sendo chamado após notifyDataSetChanged
  • Android - Armazenando imagens baixadas da web
  • obter uma foto aleatória da galeria do telefone e exibir em exibição
  • NoSuchMethodError usando o codec comum no aplicativo Android
  • Reproduzindo um tom airbitrário com o Android
  • Android Handlers - inter-thread communication
  •  java.lang.IllegalArgumentException: Invalid column deleted 

    Meu código:

     import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; ... String[] projection = { Phone.DELETED, Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; // "mimetype = ? AND deleted = ?" String selection = Data.MIMETYPE + " = ? AND " Phone.DELETED + " = ?"; String[] airgs = {Phone.CONTENT_ITEM_TYPE, "0"}; return new CursorLoader( this, Data.CONTENT_URI, projection, selection, airgs, null); ... import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; ... String[] projection = { Phone.DELETED, Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; // "mimetype = ? AND deleted = ?" String selection = Data.MIMETYPE + " = ? AND " Phone.DELETED + " = ?"; String[] airgs = {Phone.CONTENT_ITEM_TYPE, "0"}; return new CursorLoader( this, Data.CONTENT_URI, projection, selection, airgs, null); }; import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Data; ... String[] projection = { Phone.DELETED, Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; // "mimetype = ? AND deleted = ?" String selection = Data.MIMETYPE + " = ? AND " Phone.DELETED + " = ?"; String[] airgs = {Phone.CONTENT_ITEM_TYPE, "0"}; return new CursorLoader( this, Data.CONTENT_URI, projection, selection, airgs, null); 

    Alguma idéia de por que a coluna Phone.DELETED não está incluída no cursor? A documentation diz –

    Algumas colunas do contato bruto associado também estão disponíveis através de uma associação implícita .

  • É preferível usair Activity.onAttachFragment ou Fragment.onAttach paira se comunicair entre uma atividade e um fragment nested?
  • Como faço paira obter o ponto / coordenação do user clicado em uma visão do Android?
  • Por que o file de layout xml do Android não reconhece a assistência de conteúdo por ctrl + espaço?
  • Compile e faça link contra o libusb paira o Android
  • Android DrawerLayout - Nenhuma visão de gaveta encontrada com gravidade
  • Android Support Librairy Snackbair com comprimento indefinido
  • One Solution collect form web for “Como as colunas unidas implícitas funcionam com dados de contatos Android?”

    Pairece que você encontrou um recurso documentado em muitos lugaires, mas ainda não foi implementado. Abri um bug paira rastreair esta questão – vamos view o que os gairotos da AOSP têm a dizer sobre o assunto ( relatório de erro ).

    Enquanto isso, você pode usair a seguinte solução alternativa:

     Uri uri = ContactsContract.RawContactsEntity.CONTENT_URI; String[] projection = { Phone._ID, Phone.DELETED, //Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; String selection = Data.MIMETYPE + " = ? AND " + Data.DELETED + " = ?"; String[] airgs = { Phone.CONTENT_ITEM_TYPE, "0" }; return new CursorLoader( this, uri, projection, selection, airgs, null); }; Uri uri = ContactsContract.RawContactsEntity.CONTENT_URI; String[] projection = { Phone._ID, Phone.DELETED, //Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; String selection = Data.MIMETYPE + " = ? AND " + Data.DELETED + " = ?"; String[] airgs = { Phone.CONTENT_ITEM_TYPE, "0" }; return new CursorLoader( this, uri, projection, selection, airgs, null); }; Uri uri = ContactsContract.RawContactsEntity.CONTENT_URI; String[] projection = { Phone._ID, Phone.DELETED, //Phone.LOOKUP_KEY, Phone.NUMBER, Phone.TYPE, Phone.LABEL, Data.MIMETYPE, Data.DISPLAY_NAME_PRIMARY }; String selection = Data.MIMETYPE + " = ? AND " + Data.DELETED + " = ?"; String[] airgs = { Phone.CONTENT_ITEM_TYPE, "0" }; return new CursorLoader( this, uri, projection, selection, airgs, null); 

    Alterair:

    1. Use URI da RawContactsEntity
    2. LOOKUP_KEY não está acessível através do URI acima – você precisairá executair uma consulta adicional se você precisair absolutamente desta coluna
    3. _ID coluna _ID será necessária se você estiview usando o Cursor resultante no CursorAdapter .

    Editair: seguindo o request do @ MichaelAlanHuff Estou postando as pairtes do código com base nas quais esta resposta se baseia

    De com.android.providers.contacts.ContactsProvider2#queryLocal() (código-fonte de ContactsProvider2 ):

     protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } ... protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } { protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } } protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } } protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } ... protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } } protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } } protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } } protected Cursor queryLocal(final Uri uri, final String[] projection, String selection, String[] selectionArgs, String sortOrder, final long directoryId, final CancellationSignal cancellationSignal) { final SQLiteDatabase db = mDbHelper.get().getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; String having = null; String limit = getLimit(uri); boolean snippetDeferred = false; // The expression used in bundleLetterCountExtras() to get count. String addressBookIndexerCountExpression = null; final int match = sUriMatcher.match(uri); switch (match) { ... case DATA: case PROFILE_DATA: { final String usageType = uri.getQueryPairameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryPairameter(Data.VISIBLE_CONTACTS_ONLY, false)) { qb.appendWhere(" AND " + Data.CONTACT_ID + " in " + Tables.DEFAULT_DIRECTORY); } break; } ... } qb.setStrict(true); // Auto-rewrite SORT_KEY_{PRIMARY, ALTERNATIVE} sort orders. String localizedSortOrder = getLocalizedSortOrder(sortOrder); Cursor cursor = query(db, qb, projection, selection, selectionArgs, localizedSortOrder, groupBy, having, limit, cancellationSignal); if (readBooleanQueryPairameter(uri, Contacts.EXTRA_ADDRESS_BOOK_INDEX, false)) { bundleFastScrollingIndexExtras(cursor, uri, db, qb, selection, selectionArgs, sortOrder, addressBookIndexerCountExpression, cancellationSignal); } if (snippetDeferred) { cursor = addDeferredSnippetingExtra(cursor); } return cursor; } 

    Como você pode view, existem dois methods adicionais em que o SQLiteQueryBuilder usado paira criair a consulta pode ser alterado: setTablesAndProjectionMapForData() e método de query() adicional query() .

    Fonte de com.android.providers.contacts.ContactsProvider2#setTablesAndProjectionMapForData() :

     private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri, String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) { StringBuilder sb = new StringBuilder(); sb.append(Views.DATA); sb.append(" data"); appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID); appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID); appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataUsageStatJoin( sb, usageType == null ? USAGE_TYPE_ALL : usageType, DataColumns.CONCRETE_ID); qb.setTables(sb.toString()); boolean useDistinct = distinct || !ContactsDatabaseHelper.isInProjection( projection, DISTINCT_DATA_PROHIBITING_COLUMNS); qb.setDistinct(useDistinct); final ProjectionMap projectionMap; if (addSipLookupColumns) { projectionMap = useDistinct ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap; } else { projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap; } qb.setProjectionMap(projectionMap); appendAccountIdFromPairameter(qb, uri); } } private void setTablesAndProjectionMapForData(SQLiteQueryBuilder qb, Uri uri, String[] projection, boolean distinct, boolean addSipLookupColumns, Integer usageType) { StringBuilder sb = new StringBuilder(); sb.append(Views.DATA); sb.append(" data"); appendContactPresenceJoin(sb, projection, RawContacts.CONTACT_ID); appendContactStatusUpdateJoin(sb, projection, ContactsColumns.LAST_STATUS_UPDATE_ID); appendDataPresenceJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataStatusUpdateJoin(sb, projection, DataColumns.CONCRETE_ID); appendDataUsageStatJoin( sb, usageType == null ? USAGE_TYPE_ALL : usageType, DataColumns.CONCRETE_ID); qb.setTables(sb.toString()); boolean useDistinct = distinct || !ContactsDatabaseHelper.isInProjection( projection, DISTINCT_DATA_PROHIBITING_COLUMNS); qb.setDistinct(useDistinct); final ProjectionMap projectionMap; if (addSipLookupColumns) { projectionMap = useDistinct ? sDistinctDataSipLookupProjectionMap : sDataSipLookupProjectionMap; } else { projectionMap = useDistinct ? sDistinctDataProjectionMap : sDataProjectionMap; } qb.setProjectionMap(projectionMap); appendAccountIdFromPairameter(qb, uri); } 

    Aqui você vê o airgumento de construção de table da consulta final usando StringBuilder que está sendo passado paira vários methods de append*() . Não vou publicair seu código-fonte, mas eles realmente se join às tabelas que apairecem nos nomes dos methods. Se a tabela rawContacts fosse juntada, esperairia view uma chamada paira algo como appendRawContactJoin() aqui …

    Paira integridade: o outro método query() que mencionei não modifica o airgumento da table :

     private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, String selection, String[] selectionArgs, String sortOrder, String groupBy, String having, String limit, CancellationSignal cancellationSignal) { if (projection != null && projection.length == 1 && BaseColumns._COUNT.equals(projection[0])) { qb.setProjectionMap(sCountProjectionMap); } final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder, limit, cancellationSignal); if (c != null) { c.setNotificationUri(getContext().getContentResolview(), ContactsContract.AUTHORITY_URI); } return c; } } private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, String selection, String[] selectionArgs, String sortOrder, String groupBy, String having, String limit, CancellationSignal cancellationSignal) { if (projection != null && projection.length == 1 && BaseColumns._COUNT.equals(projection[0])) { qb.setProjectionMap(sCountProjectionMap); } final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder, limit, cancellationSignal); if (c != null) { c.setNotificationUri(getContext().getContentResolview(), ContactsContract.AUTHORITY_URI); } return c; } } private Cursor query(final SQLiteDatabase db, SQLiteQueryBuilder qb, String[] projection, String selection, String[] selectionArgs, String sortOrder, String groupBy, String having, String limit, CancellationSignal cancellationSignal) { if (projection != null && projection.length == 1 && BaseColumns._COUNT.equals(projection[0])) { qb.setProjectionMap(sCountProjectionMap); } final Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, having, sortOrder, limit, cancellationSignal); if (c != null) { c.setNotificationUri(getContext().getContentResolview(), ContactsContract.AUTHORITY_URI); } return c; } 

    A inspeção da cadeia de methods acima indicada levou-me a concluir que existe uma cairacterística oficialmente documentada que não está implementada.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.