Skip to content

Commit

Permalink
Greatly improve speed of fetching contacts.
Browse files Browse the repository at this point in the history
We were selecting every column in a fairly wide table before. This code fetches
only those columns necessary to populate the data requested by the Javascript
code. In experiments with coworkers' and my own contact lists, the time to fetch
~1440 contacts has gone from over 40 seconds to less than 10 seconds. I have not
tested with fewer than 1400 contacts, but I expect at least a small improvement.
  • Loading branch information
bshepherdson authored and macdonst committed Aug 22, 2012
1 parent 92b1de8 commit e11f8f6
Showing 1 changed file with 119 additions and 33 deletions.
152 changes: 119 additions & 33 deletions framework/src/org/apache/cordova/ContactAccessorSdk5.java
Expand Up @@ -19,6 +19,26 @@ Licensed to the Apache Software Foundation (ASF) under one

package org.apache.cordova;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Debug;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.util.Log;
import android.webkit.WebView;

import org.apache.cordova.api.CordovaInterface;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
Expand All @@ -31,27 +51,8 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.cordova.api.CordovaInterface;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.accounts.Account;
import android.accounts.AccountManager;
//import android.app.Activity;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
//import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.util.Log;
import android.webkit.WebView;

/**
* An implementation of {@link ContactAccessor} that uses current Contacts API.
Expand Down Expand Up @@ -151,6 +152,7 @@ public JSONArray search(JSONArray fields, JSONObject options) {
else {
searchTerm = "%" + searchTerm + "%";
}

try {
multiple = options.getBoolean("multiple");
if (!multiple) {
Expand All @@ -163,6 +165,7 @@ public JSONArray search(JSONArray fields, JSONObject options) {
else {
searchTerm = "%";
}


//Log.d(LOG_TAG, "Search Term = " + searchTerm);
//Log.d(LOG_TAG, "Field Length = " + fields.length());
Expand All @@ -182,19 +185,88 @@ public JSONArray search(JSONArray fields, JSONObject options) {
ContactsContract.Data.CONTACT_ID + " ASC");

// Create a set of unique ids
//Log.d(LOG_TAG, "ID cursor query returns = " + idCursor.getCount());
Set<String> contactIds = new HashSet<String>();
int idColumn = -1;
while (idCursor.moveToNext()) {
contactIds.add(idCursor.getString(idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID)));
if (idColumn < 0) {
idColumn = idCursor.getColumnIndex(ContactsContract.Data.CONTACT_ID);
}
contactIds.add(idCursor.getString(idColumn));
}
idCursor.close();

// Build a query that only looks at ids
WhereOptions idOptions = buildIdClause(contactIds, searchTerm);

// Determine which columns we should be fetching.
HashSet<String> columnsToFetch = new HashSet<String>();
columnsToFetch.add(ContactsContract.Data.CONTACT_ID);
columnsToFetch.add(ContactsContract.Data.RAW_CONTACT_ID);
columnsToFetch.add(ContactsContract.Data.MIMETYPE);

if (isRequired("displayName", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
}
if (isRequired("name", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.PREFIX);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredName.SUFFIX);
}
if (isRequired("phoneNumbers", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone._ID);
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone.NUMBER);
columnsToFetch.add(ContactsContract.CommonDataKinds.Phone.TYPE);
}
if (isRequired("emails", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Email.DATA);
columnsToFetch.add(ContactsContract.CommonDataKinds.Email.TYPE);
}
if (isRequired("addresses", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal._ID);
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TYPE);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.STREET);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.CITY);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.REGION);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE);
columnsToFetch.add(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY);
}
if (isRequired("organizations", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization._ID);
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TYPE);
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.DEPARTMENT);
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.COMPANY);
columnsToFetch.add(ContactsContract.CommonDataKinds.Organization.TITLE);
}
if (isRequired("ims", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Im._ID);
columnsToFetch.add(ContactsContract.CommonDataKinds.Im.DATA);
columnsToFetch.add(ContactsContract.CommonDataKinds.Im.TYPE);
}
if (isRequired("note", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Note.NOTE);
}
if (isRequired("nickname", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Nickname.NAME);
}
if (isRequired("urls", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Website._ID);
columnsToFetch.add(ContactsContract.CommonDataKinds.Website.URL);
columnsToFetch.add(ContactsContract.CommonDataKinds.Website.TYPE);
}
if (isRequired("birthday", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Event.START_DATE);
columnsToFetch.add(ContactsContract.CommonDataKinds.Event.TYPE);
}
if (isRequired("photos", populate)) {
columnsToFetch.add(ContactsContract.CommonDataKinds.Photo._ID);
}

// Do the id query
Cursor c = mApp.getActivity().getContentResolver().query(ContactsContract.Data.CONTENT_URI,
null,
columnsToFetch.toArray(new String[] {}),
idOptions.getWhere(),
idOptions.getWhereArgs(),
ContactsContract.Data.CONTACT_ID + " ASC");
Expand Down Expand Up @@ -259,11 +331,23 @@ private JSONArray populateContactArray(int limit,
JSONArray websites = new JSONArray();
JSONArray photos = new JSONArray();

ArrayList<String> names = new ArrayList<String>();

// Column indices
int colContactId = c.getColumnIndex(ContactsContract.Data.CONTACT_ID);
int colRawContactId = c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID);
int colMimetype = c.getColumnIndex(ContactsContract.Data.MIMETYPE);
int colDisplayName = c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME);
int colNote = c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE);
int colNickname = c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME);
int colBirthday = c.getColumnIndex(ContactsContract.CommonDataKinds.Event.START_DATE);
int colEventType = c.getColumnIndex(ContactsContract.CommonDataKinds.Event.TYPE);

if (c.getCount() > 0) {
while (c.moveToNext() && (contacts.length() <= (limit - 1))) {
try {
contactId = c.getString(c.getColumnIndex(ContactsContract.Data.CONTACT_ID));
rawId = c.getString(c.getColumnIndex(ContactsContract.Data.RAW_CONTACT_ID));
contactId = c.getString(colContactId);
rawId = c.getString(colRawContactId);

// If we are in the first row set the oldContactId
if (c.getPosition() == 0) {
Expand Down Expand Up @@ -301,11 +385,12 @@ private JSONArray populateContactArray(int limit,
}

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

mimetype = c.getString(colMimetype);
if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
contact.put("displayName", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME)));
contact.put("displayName", c.getString(colDisplayName));
}

if (mimetype.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
&& isRequired("name", populate)) {
contact.put("name", nameQuery(c));
Expand All @@ -332,20 +417,20 @@ && isRequired("ims", populate)) {
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE)
&& isRequired("note", populate)) {
contact.put("note", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Note.NOTE)));
contact.put("note", c.getString(colNote));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)
&& isRequired("nickname", populate)) {
contact.put("nickname", c.getString(c.getColumnIndex(ContactsContract.CommonDataKinds.Nickname.NAME)));
contact.put("nickname", c.getString(colNickname));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE)
&& isRequired("urls", populate)) {
websites.put(websiteQuery(c));
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE)) {
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)));
if (isRequired("birthday", populate) &&
ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY == c.getInt(colEventType)) {
contact.put("birthday", c.getString(colBirthday));
}
}
else if (mimetype.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
Expand All @@ -358,8 +443,9 @@ && isRequired("photos", populate)) {

// Set the old contact ID
oldContactId = contactId;
}

}

// Push the last contact into the contacts array
if (contacts.length() < limit) {
contacts.put(populateContact(contact, organizations, addresses, phones,
Expand Down

0 comments on commit e11f8f6

Please sign in to comment.