How do implicit joined columns work with Android contacts data? -


i'm querying contactscontract.data table find phone records.

i error when create new cursorloader:

java.lang.illegalargumentexception: invalid column deleted 

my code:

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 = ? , deleted = ?" string selection = data.mimetype + " = ? , " phone.deleted + " = ?";   string[] args = {phone.content_item_type, "0"};  return new cursorloader(     this,     data.content_uri,     projection,     selection,     args,     null); 

any idea why phone.deleted column isn't included in cursor? documentation -

some columns associated raw contact available through implicit join.

looks you've found feature has been documented in many places, hadn't been implemented yet. opened bug tracking issue - lets see aosp guys have on subject (bug report).

meanwhile, can use following workaround:

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 + " = ? , " + data.deleted + " = ?"; string[] args = {     phone.content_item_type, "0" };  return new cursorloader( this, uri, projection, selection, args, null); 

changes:

  1. use rawcontactsentity's uri
  2. lookup_key not accessible via above uri - you'll have execute additional query if absolutely need column
  3. _id column required if going use resulting cursor in cursoradapter.

edit: following @michaelalanhuff's request i'm posting parts of code answer based upon

from com.android.providers.contacts.contactsprovider2#querylocal() (source code of 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;      // expression used in bundlelettercountextras() 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(" , " + 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;   } 

as can see, there 2 additional methods sqlitequerybuilder used build query changed: settablesandprojectionmapfordata() , additional query() method.

source of 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); } 

here see construction of table argument of final query using stringbuilder being passed several append*() methods. i'm not going post source code, join tables appear in methods' names. if rawcontacts table joined in, i'd expect see call appendrawcontactjoin() here...

for completeness: other query() method mentioned not modify table argument:

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

the inspection of above chain of methods led me conclusion there officially documented feature not implemented.


Comments

Popular posts from this blog

python - TypeError: start must be a integer -

c# - DevExpress RepositoryItemComboBox BackColor property ignored -

django - Creating multiple model instances in DRF3 -