Reduced everything to single database query

This commit is contained in:
macdonst 2010-10-08 23:04:49 +08:00
parent 4a6105de6b
commit 34859ec479
2 changed files with 252 additions and 204 deletions

View File

@ -159,4 +159,21 @@ public abstract class ContactAccessor {
* Handles removing a contact from the database. * Handles removing a contact from the database.
*/ */
public abstract boolean remove(String id); public abstract boolean remove(String id);
class WhereOptions {
private String where;
private String[] whereArgs;
public void setWhere(String where) {
this.where = where;
}
public String getWhere() {
return where;
}
public void setWhereArgs(String[] whereArgs) {
this.whereArgs = whereArgs;
}
public String[] getWhereArgs() {
return whereArgs;
}
}
} }

View File

@ -17,20 +17,16 @@
package com.phonegap; package com.phonegap;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import android.app.Activity; import android.app.Activity;
import android.content.ContentResolver;
import android.database.Cursor; import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.util.Log; import android.util.Log;
import android.webkit.WebView; import android.webkit.WebView;
@ -112,7 +108,11 @@ public class ContactAccessorSdk5 extends ContactAccessor {
} }
@Override @Override
public JSONArray search(JSONArray filter, JSONObject options) { public JSONArray search(JSONArray fields, JSONObject options) {
long totalEnd;
long totalStart = System.currentTimeMillis();
// Get the find options
String searchTerm = ""; String searchTerm = "";
int limit = Integer.MAX_VALUE; int limit = Integer.MAX_VALUE;
boolean multiple = true; boolean multiple = true;
@ -131,252 +131,283 @@ public class ContactAccessorSdk5 extends ContactAccessor {
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e); Log.e(LOG_TAG, e.getMessage(), e);
} }
// Get a cursor by creating the query.
ContentResolver cr = mApp.getContentResolver();
Set<String> contactIds = buildSetOfContactIds(filter, searchTerm);
HashMap<String,Boolean> populate = buildPopulationSet(filter);
Iterator<String> it = contactIds.iterator();
JSONArray contacts = new JSONArray();
String contactId; // Loop through the fields the user provided to see what data should be returned.
int pos = 0; HashMap<String,Boolean> populate = buildPopulationSet(fields);
boolean firstRow = true;
while (it.hasNext() && (pos < limit)) { // Build the ugly where clause and where arguments for one big query.
JSONObject contact = new JSONObject(); WhereOptions whereOptions = buildWhereClause(fields, searchTerm);
JSONArray organizations = new JSONArray();
JSONArray addresses = new JSONArray();
JSONArray phones = new JSONArray();
JSONArray emails = new JSONArray();
JSONArray ims = new JSONArray();
JSONArray websites = new JSONArray();
JSONArray relationships = new JSONArray();
contactId = it.next(); // Get all the rows where the search term matches the fields passed in.
Cursor c = mApp.getContentResolver().query(ContactsContract.Data.CONTENT_URI,
Cursor c = cr.query(ContactsContract.Data.CONTENT_URI, null,
null, whereOptions.getWhere(),
ContactsContract.Data.CONTACT_ID + " = ?", whereOptions.getWhereArgs(),
new String[] {contactId}, ContactsContract.Data.CONTACT_ID + " ASC");
null);
String contactId = "";
String mimetype = ""; String oldContactId = "";
while (c.moveToNext()) { boolean newContact = true;
try { String mimetype = "";
if (firstRow) {
firstRow = false; JSONArray contacts = new JSONArray();
contact.put("id", contactId); JSONObject contact = new JSONObject();
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))); JSONArray organizations = new JSONArray();
} JSONArray addresses = new JSONArray();
mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE)); JSONArray phones = new JSONArray();
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) JSONArray emails = new JSONArray();
&& isRequired("name",populate)) { JSONArray ims = new JSONArray();
contact.put("name", nameQuery(c)); JSONArray websites = new JSONArray();
} JSONArray relationships = new JSONArray();
if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
&& isRequired("phoneNumbers",populate)) { while (c.moveToNext() && (contacts.length() < (limit-1))) {
phones.put(phoneQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
&& isRequired("emails",populate)) {
emails.put(emailQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
&& isRequired("addresses",populate)) {
addresses.put(addressQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
&& isRequired("organizations",populate)) {
organizations.put(organizationQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
&& isRequired("ims",populate)) {
ims.put(imQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
&& isRequired("note",populate)) {
contact.put("note",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
&& isRequired("nickname",populate)) {
contact.put("nickname",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
&& isRequired("urls",populate)) {
websites.put(websiteQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE)
&& isRequired("relationships",populate)) {
relationships.put(relationshipQuery(c));
}
if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
if (ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
&& isRequired("anniversary",populate)) {
contact.put("anniversary", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
}
else if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
&& isRequired("birthday",populate)) {
contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
}
}
}
catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(),e);
}
}
c.close();
firstRow = true;
// Populate the Contact object with it's arrays
try { try {
contact.put("organizations", organizations); contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
contact.put("addresses", addresses);
contact.put("phoneNumbers", phones); // If we are in the first row set the oldContactId
contact.put("emails", emails); if (c.getPosition() == 0) {
contact.put("ims", ims); oldContactId = contactId;
contact.put("websites", websites); }
contact.put("relationships", relationships);
// When the contact ID changes we need to push the Contact object
// to the array of contacts and create new objects.
if (!oldContactId.equals(contactId)) {
// Populate the Contact object with it's arrays
// and push the contact into the contacts array
contacts.put(populateContact(contact, organizations, addresses, phones,
emails, ims, websites, relationships));
// Clean up the objects
contact = new JSONObject();
organizations = new JSONArray();
addresses = new JSONArray();
phones = new JSONArray();
emails = new JSONArray();
ims = new JSONArray();
websites = new JSONArray();
relationships = new JSONArray();
// Set newContact to true as we are starting to populate a new contact
newContact = true;
}
// When we detect a new contact set the ID and display name.
// These fields are available in every row in the result set returned.
if (newContact) {
newContact = false;
contact.put("id", contactId);
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)));
}
// Grab the mimetype of the current row as it will be used in a lot of comparisons
mimetype = c.getString(c.getColumnIndex(ContactsContract.Data.MIMETYPE));
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
&& isRequired("name",populate)) {
contact.put("name", nameQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
&& isRequired("phoneNumbers",populate)) {
phones.put(phoneQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
&& isRequired("emails",populate)) {
emails.put(emailQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)
&& isRequired("addresses",populate)) {
addresses.put(addressQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE)
&& isRequired("organizations",populate)) {
organizations.put(organizationQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE)
&& isRequired("ims",populate)) {
ims.put(imQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
&& isRequired("note",populate)) {
contact.put("note",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
&& isRequired("nickname",populate)) {
contact.put("nickname",c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
&& isRequired("urls",populate)) {
websites.put(websiteQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE)
&& isRequired("relationships",populate)) {
relationships.put(relationshipQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
if (ContactsContract.CommonDataKinds.Event.TYPE_ANNIVERSARY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
&& isRequired("anniversary",populate)) {
contact.put("anniversary", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
}
else if (ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE))
&& isRequired("birthday",populate)) {
contact.put("birthday", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE)));
}
}
} }
catch (JSONException e) { catch (JSONException e) {
Log.e(LOG_TAG,e.getMessage(),e); Log.e(LOG_TAG, e.getMessage(),e);
} }
contacts.put(contact);
pos++; // Set the old contact ID
oldContactId = contactId;
} }
c.close();
// Push the last contact into the contacts array
contacts.put(populateContact(contact, organizations, addresses, phones,
emails, ims, websites, relationships));
totalEnd = System.currentTimeMillis();
Log.d(LOG_TAG,"Total time = " + (totalEnd-totalStart));
return contacts; return contacts;
} }
private Set<String> buildSetOfContactIds(JSONArray filter, String searchTerm) { private JSONObject populateContact(JSONObject contact, JSONArray organizations,
Set<String> contactIds = new HashSet<String>(); JSONArray addresses, JSONArray phones, JSONArray emails,
JSONArray ims, JSONArray websites, JSONArray relationships) {
try {
contact.put("organizations", organizations);
contact.put("addresses", addresses);
contact.put("phoneNumbers", phones);
contact.put("emails", emails);
contact.put("ims", ims);
contact.put("websites", websites);
contact.put("relationships", relationships);
}
catch (JSONException e) {
Log.e(LOG_TAG,e.getMessage(),e);
}
return contact;
}
private WhereOptions buildWhereClause(JSONArray filter, String searchTerm) {
ArrayList<String> where = new ArrayList<String>();
ArrayList<String> whereArgs = new ArrayList<String>();
WhereOptions options = new WhereOptions();
/* /*
* Special case for when the user wants all the contacts * Special case for when the user wants all the contacts
*/ */
if ("%".equals(searchTerm)) { if ("%".equals(searchTerm)) {
doQuery(searchTerm, contactIds, options.setWhere("(" + ContactsContract.Contacts.DISPLAY_NAME + " LIKE ? )");
ContactsContract.Contacts.CONTENT_URI, options.setWhereArgs(new String[] {searchTerm});
ContactsContract.Contacts._ID, return options;
ContactsContract.Contacts.DISPLAY_NAME + " LIKE ?", }
new String[] {searchTerm});
return contactIds;
}
String key; String key;
try { try {
for (int i=0; i<filter.length(); i++) { for (int i=0; i<filter.length(); i++) {
key = filter.getString(i); key = filter.getString(i);
if (key.startsWith("displayName")) { if (key.startsWith("displayName")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? )");
ContactsContract.Contacts.CONTENT_URI, whereArgs.add(searchTerm);
ContactsContract.Contacts._ID,
dbMap.get(key) + " LIKE ?",
new String[] {searchTerm});
} }
else if (key.startsWith("name")) { else if (key.startsWith("name")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("nickname")) { else if (key.startsWith("nickname")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("phoneNumbers")) { else if (key.startsWith("phoneNumbers")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.CommonDataKinds.Phone.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ?", whereArgs.add(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
new String[] {searchTerm});
} }
else if (key.startsWith("emails")) { else if (key.startsWith("emails")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.CommonDataKinds.Email.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.CommonDataKinds.Email.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ?", whereArgs.add(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
new String[] {searchTerm});
} }
else if (key.startsWith("addresses")) { else if (key.startsWith("addresses")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("ims")) { else if (key.startsWith("ims")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("organizations")) { else if (key.startsWith("organizations")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE});
}
else if (key.startsWith("birthday")) {
}
else if (key.startsWith("anniversary")) {
} }
// else if (key.startsWith("birthday")) {
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
// + ContactsContract.Data.MIMETYPE + " = ? )");
// }
// else if (key.startsWith("anniversary")) {
// where.add("(" + dbMap.get(key) + " LIKE ? AND "
// + ContactsContract.Data.MIMETYPE + " = ? )");
// whereArgs.add(searchTerm);
// whereArgs.add(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
// }
else if (key.startsWith("note")) { else if (key.startsWith("note")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("relationships")) { else if (key.startsWith("relationships")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Relation.CONTENT_ITEM_TYPE});
} }
else if (key.startsWith("urls")) { else if (key.startsWith("urls")) {
doQuery(searchTerm, contactIds, where.add("(" + dbMap.get(key) + " LIKE ? AND "
ContactsContract.Data.CONTENT_URI, + ContactsContract.Data.MIMETYPE + " = ? )");
ContactsContract.Data.CONTACT_ID, whereArgs.add(searchTerm);
dbMap.get(key) + " LIKE ? AND " + ContactsContract.Data.MIMETYPE + " = ?", whereArgs.add(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE);
new String[] {searchTerm, ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE});
} }
} }
} }
catch (JSONException e) { catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e); Log.e(LOG_TAG, e.getMessage(), e);
} }
return contactIds;
}
private void doQuery(String searchTerm, Set<String> contactIds, // Creating the where string
Uri uri, String projection, String selection, String[] selectionArgs) { StringBuffer selection = new StringBuffer();
// Get a cursor by creating the query. for (int i=0; i<where.size(); i++) {
ContentResolver cr = mApp.getContentResolver(); selection.append(where.get(i));
if (i != (where.size()-1)) {
Cursor cursor = cr.query( selection.append(" OR ");
uri, }
new String[] {projection},
selection,
selectionArgs,
null);
while (cursor.moveToNext()) {
contactIds.add(cursor.getString(cursor.getColumnIndex(projection)));
} }
cursor.close(); options.setWhere(selection.toString());
// Creating the where args array
String[] selectionArgs = new String[whereArgs.size()];
for (int i=0; i<whereArgs.size(); i++) {
selectionArgs[i] = whereArgs.get(i);
}
options.setWhereArgs(selectionArgs);
return options;
} }
private JSONObject organizationQuery(Cursor cursor) { private JSONObject organizationQuery(Cursor cursor) {