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:
- Usar RawContactsEntity''s URI RawContactsEntity''s
-
LOOKUP_KEY
se puede acceder aLOOKUP_KEY
través del URI anterior. Tendrá que ejecutar una consulta adicional si necesita absolutamente esta columna -
_ID
columna_ID
será necesaria si va a utilizar elCursor
resultante enCursorAdapter
.
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 .