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:
- use rawcontactsentity's uri
lookup_key
not accessible via above uri - you'll have execute additional query if absolutely need column_id
column required if going use resultingcursor
incursoradapter
.
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
Post a Comment