20

Sto interrogando la tabella ContactsContract.Data per trovare i record del telefono.In che modo le colonne unite implicite funzionano con i dati dei contatti Android?

ottengo un errore quando creo un nuovo CursorLoader:

java.lang.IllegalArgumentException: Invalid column deleted 

Il mio codice:

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); 

Qualsiasi idea del perché la colonna Phone.DELETED non è incluso nel cursore? Il documentation fa dire -

Alcune colonne dal contatto grezzo associato sono anche disponibili attraverso una implicita aderire.

+0

Si tratta di più dispositivi? –

+0

@MichaelAlanHuff - sì, ho provato su due dispositivi. Android 5.0 e 5.1. – Gautam

risposta

5

Sembra che tu abbia trovato una funzione che è stata documentata in molti posti, ma che non era ancora stata implementata. Ho aperto un bug per tenere traccia di questo problema - vediamo cosa hanno da dire i ragazzi AOSP sull'argomento (bug report).

Nel frattempo, è possibile utilizzare la seguente soluzione:

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); 

Modifiche:

  1. Usa RawContactsEntity's URI
  2. LOOKUP_KEY non è accessibile via sopra URI - dovrete eseguire query aggiuntiva se hai assolutamente bisogno di questa colonna
  3. _ID colonna sarà necessaria se si intende utilizzare th e risultante Cursor in CursorAdapter.

Edit: dopo @ richiesta di MichaelAlanHuff sto postando le parti di codice che questa risposta si basa su

Da com.android.providers.contacts.ContactsProvider2#queryLocal() (codice sorgente di 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; 


} 

Come si può vedere, ci sono due metodi aggiuntivi in ​​cui SQLiteQueryBuilder utilizzato per costruire la query potrebbe essere modificato: setTablesAndProjectionMapForData() e ulteriore metodo.

Fonte di 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); 
} 

Qui si vede la costruzione di table argomento della query finale utilizzando StringBuilder che viene passato a diversi append*() metodi. Non pubblicherò il loro codice sorgente, ma sono davvero le join le tabelle che compaiono nei nomi dei metodi. Se la tabella rawContacts si unisse, mi aspetterei di vedere una chiamata a qualcosa come appendRawContactJoin() qui ...

Per completezza: l'altra query() metodo che ho citato non modifica table argomento:

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; 
} 

L'ispezione della catena sopra dei metodi mi ha portato alla conclusione che v'è una caratteristica ufficialmente documentato che non è implementato.

+0

Grazie per la risposta. Non mi aspettavo che qualcosa fosse documentato e non implementato. Di solito è il contrario. :) – Gautam

+0

Btw, hai qualche idea di cosa potrebbe essere sbagliato qui: http://stackoverflow.com/q/30783516/886468 – Gautam

+2

@ Gautam, sono d'accordo che questo è molto insolito. Ho fatto diversi avanti e indietro al codice sorgente perché non riuscivo a credere che fosse così. Tieni d'occhio il bug che ho aperto - forse ho ancora qualcosa nel modo sbagliato. – Vasiliy

Problemi correlati