studio seleccionar read obtener lista example content contactos android android-contentprovider contacts android-contacts contactscontract

seleccionar - ¿Cómo funcionan las columnas unidas implícitas con los datos de contactos de Android?



read contacts android studio (1)

Parece que has encontrado una característica que se ha documentado en muchos lugares, pero que aún no se ha implementado. Abrí un error para rastrear este problema: veamos lo que dicen los chicos de AOSP sobre el tema ( informe de error ).

Mientras tanto, puedes usar la siguiente solución:

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[] args = { Phone.CONTENT_ITEM_TYPE, "0" }; return new CursorLoader( this, uri, projection, selection, args, null);

Cambios:

  1. Usar RawContactsEntity''s URI RawContactsEntity''s
  2. LOOKUP_KEY se puede acceder a LOOKUP_KEY través del URI anterior. Tendrá que ejecutar una consulta adicional si necesita absolutamente esta columna
  3. _ID columna _ID será necesaria si va a utilizar el Cursor resultante en CursorAdapter .

Edición: siguiendo la solicitud de @ MichaelAlanHuff, estoy publicando las partes del código en las que se basa esta respuesta

Desde com.android.providers.contacts.ContactsProvider2#queryLocal() (código fuente 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.getQueryParameter(DataUsageFeedback.USAGE_TYPE); final int typeInt = getDataUsageFeedbackType(usageType, USAGE_TYPE_ALL); setTablesAndProjectionMapForData(qb, uri, projection, false, typeInt); if (uri.getBooleanQueryParameter(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 (readBooleanQueryParameter(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 puede ver, hay dos métodos adicionales en los que se puede cambiar SQLiteQueryBuilder utilizado para crear la consulta: setTablesAndProjectionMapForData() y el método de query() adicional query() .

Fuente 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); appendAccountIdFromParameter(qb, uri); }

Aquí puede ver la construcción del argumento de la table de la consulta final utilizando StringBuilder que se está pasando a varios métodos append*() . No voy a publicar su código fuente, pero realmente se join las tablas que aparecen en los nombres de los métodos. Si la tabla rawContacts se uniera, esperaría ver una llamada a algo como appendRawContactJoin() aquí ...

Para completar: el otro método de query() que mencioné no modifica el argumento de la 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().getContentResolver(), ContactsContract.AUTHORITY_URI); } return c; }

La inspección de la cadena de métodos anterior me llevó a la conclusión de que hay una característica documentada oficialmente que no está implementada.

Estoy consultando la tabla ContactsContract.Data para encontrar registros telefónicos.

Recibo un error cuando creo un nuevo CursorLoader :

java.lang.IllegalArgumentException: Invalid column deleted

Mi 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[] args = {Phone.CONTENT_ITEM_TYPE, "0"}; return new CursorLoader( this, Data.CONTENT_URI, projection, selection, args, null);

¿Alguna idea de por qué la columna Phone.DELETED no está incluida en el cursor? La documentation dice:

Algunas columnas del contacto directo asociado también están disponibles a través de una unión implícita .