summaryrefslogtreecommitdiffstats
path: root/src/com/android/providers
diff options
context:
space:
mode:
authorDmitri Plotnikov <dplotnikov@google.com>2009-09-20 16:56:40 -0700
committerDmitri Plotnikov <dplotnikov@google.com>2009-09-21 09:49:52 -0700
commitf23764675b35b5262a39c79aad8e9842460274b2 (patch)
tree7c5da61a6713912dc6ace1777e15760169526211 /src/com/android/providers
parentd15a13a9b1dfe59dcf7196c6d5d55afe7f25d2a2 (diff)
downloadpackages_providers_ContactsProvider-f23764675b35b5262a39c79aad8e9842460274b2.zip
packages_providers_ContactsProvider-f23764675b35b5262a39c79aad8e9842460274b2.tar.gz
packages_providers_ContactsProvider-f23764675b35b5262a39c79aad8e9842460274b2.tar.bz2
No longer relying on the components of structured name for aggregation.
Now parsing display name into tokens and allowing permutations of those. Bug IDs: 2132657, 2132636, 2089893 Change-Id: Idea256bbec3b82fb229199c6bd6e9d7b145ab075
Diffstat (limited to 'src/com/android/providers')
-rw-r--r--src/com/android/providers/contacts/ContactAggregator.java199
-rw-r--r--src/com/android/providers/contacts/ContactsProvider2.java199
-rw-r--r--src/com/android/providers/contacts/GlobalSearchSupport.java16
-rw-r--r--src/com/android/providers/contacts/LegacyApiSupport.java6
-rw-r--r--src/com/android/providers/contacts/LegacyContactImporter.java11
-rw-r--r--src/com/android/providers/contacts/NameLookupBuilder.java166
-rw-r--r--src/com/android/providers/contacts/NameNormalizer.java9
-rw-r--r--src/com/android/providers/contacts/NameSplitter.java35
-rw-r--r--src/com/android/providers/contacts/OpenHelper.java199
9 files changed, 483 insertions, 357 deletions
diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java
index e16c615..27295f4 100644
--- a/src/com/android/providers/contacts/ContactAggregator.java
+++ b/src/com/android/providers/contacts/ContactAggregator.java
@@ -54,7 +54,6 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -70,40 +69,46 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
private static final String TAG = "ContactAggregator";
- // Data mime types used in the contact matching algorithm
- private static final String MIMETYPE_SELECTION_IN_CLAUSE = MimetypesColumns.MIMETYPE + " IN ('"
- + Email.CONTENT_ITEM_TYPE + "','"
- + Nickname.CONTENT_ITEM_TYPE + "','"
- + Phone.CONTENT_ITEM_TYPE + "','"
- + StructuredName.CONTENT_ITEM_TYPE + "')";
+ private interface DataMimetypeQuery {
- private static final String[] DATA_JOIN_MIMETYPE_COLUMNS = new String[] {
- MimetypesColumns.MIMETYPE,
- Data.DATA1,
- Data.DATA2
- };
+ // Data mime types used in the contact matching algorithm
+ String MIMETYPE_SELECTION_IN_CLAUSE = MimetypesColumns.MIMETYPE + " IN ('"
+ + Email.CONTENT_ITEM_TYPE + "','"
+ + Nickname.CONTENT_ITEM_TYPE + "','"
+ + Phone.CONTENT_ITEM_TYPE + "','"
+ + StructuredName.CONTENT_ITEM_TYPE + "')";
- private static final int COL_MIMETYPE = 0;
- private static final int COL_DATA1 = 1;
- private static final int COL_DATA2 = 2;
+ String[] COLUMNS = new String[] {
+ MimetypesColumns.MIMETYPE, Data.DATA1
+ };
- private static final String[] DATA_JOIN_MIMETYPE_AND_CONTACT_COLUMNS = new String[] {
- Data.DATA1, Data.DATA2, RawContacts.CONTACT_ID
- };
+ int MIMETYPE = 0;
+ int DATA1 = 1;
+ }
- private static final int COL_DATA_CONTACT_DATA1 = 0;
- private static final int COL_DATA_CONTACT_DATA2 = 1;
- private static final int COL_DATA_CONTACT_CONTACT_ID = 2;
+ private interface DataContactIdQuery {
+ String TABLE = Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS;
- private static final String[] NAME_LOOKUP_COLUMNS = new String[] {
- RawContacts.CONTACT_ID, NameLookupColumns.NORMALIZED_NAME, NameLookupColumns.NAME_TYPE
- };
+ String[] COLUMNS = new String[] {
+ Data.DATA1, RawContacts.CONTACT_ID
+ };
- private static final int COL_NAME_LOOKUP_CONTACT_ID = 0;
- private static final int COL_NORMALIZED_NAME = 1;
- private static final int COL_NAME_TYPE = 2;
+ int DATA1 = 0;
+ int CONTACT_ID = 1;
+ }
- private static final String[] CONTACT_ID_COLUMN = new String[] { RawContacts._ID };
+ private interface NameLookupQuery {
+ String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
+
+ String[] COLUMNS = new String[] {
+ RawContacts.CONTACT_ID, NameLookupColumns.NORMALIZED_NAME,
+ NameLookupColumns.NAME_TYPE
+ };
+
+ int CONTACT_ID = 0;
+ int NORMALIZED_NAME = 1;
+ int NAME_TYPE = 2;
+ }
private interface EmailLookupQuery {
String TABLE = Tables.DATA_JOIN_RAW_CONTACTS;
@@ -115,12 +120,12 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
int CONTACT_ID = 0;
}
+ private static final String[] CONTACT_ID_COLUMN = new String[] { RawContacts._ID };
private static final String[] CONTACT_ID_COLUMNS = new String[]{ RawContacts.CONTACT_ID };
private static final int COL_CONTACT_ID = 0;
- private static final int MODE_INSERT_LOOKUP_DATA = 0;
- private static final int MODE_AGGREGATION = 1;
- private static final int MODE_SUGGESTIONS = 2;
+ private static final int MODE_AGGREGATION = 0;
+ private static final int MODE_SUGGESTIONS = 1;
/**
* When yielding the transaction to another thread, sleep for this many milliseconds
@@ -209,6 +214,27 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
}
}
+ private class AggregationNameLookupBuilder extends NameLookupBuilder {
+
+ private final MatchCandidateList mCandidates;
+
+ public AggregationNameLookupBuilder(NameSplitter splitter, MatchCandidateList candidates) {
+ super(splitter);
+ mCandidates = candidates;
+ }
+
+ @Override
+ protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
+ String name) {
+ mCandidates.add(name, lookupType);
+ }
+
+ @Override
+ protected String[] getCommonNicknameClusters(String normalizedName) {
+ return mContactsProvider.getCommonNicknameClusters(normalizedName);
+ }
+ }
+
/**
* Constructor. Starts a contact aggregation thread. Call {@link #quit} to kill the
* aggregation thread. Call {@link #schedule} to kick off the aggregation process after
@@ -752,20 +778,19 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
selection.append(") AND " + MimetypesColumns.MIMETYPE + "='"
+ StructuredName.CONTENT_ITEM_TYPE + "'");
- final Cursor c = db.query(Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS,
- DATA_JOIN_MIMETYPE_AND_CONTACT_COLUMNS,
+ final Cursor c = db.query(DataContactIdQuery.TABLE, DataContactIdQuery.COLUMNS,
selection.toString(), null, null, null, null);
MatchCandidateList nameCandidates = new MatchCandidateList();
+ AggregationNameLookupBuilder builder =
+ new AggregationNameLookupBuilder(mContactsProvider.getNameSplitter(), nameCandidates);
try {
while (c.moveToNext()) {
- String givenName = c.getString(COL_DATA_CONTACT_DATA1);
- String familyName = c.getString(COL_DATA_CONTACT_DATA2);
- long contactId = c.getLong(COL_DATA_CONTACT_CONTACT_ID);
+ String name = c.getString(DataContactIdQuery.DATA1);
+ long contactId = c.getLong(DataContactIdQuery.CONTACT_ID);
nameCandidates.clear();
- addMatchCandidatesStructuredName(givenName, familyName, MODE_INSERT_LOOKUP_DATA,
- nameCandidates);
+ builder.insertNameLookup(0, 0, name);
// Note the N^2 complexity of the following fragment. This is not a huge concern
// since the number of candidates is very small and in general secondary hits
@@ -800,31 +825,30 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
int mode, MatchCandidateList candidates, ContactMatcher matcher) {
final Cursor c = db.query(Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS,
- DATA_JOIN_MIMETYPE_COLUMNS,
+ DataMimetypeQuery.COLUMNS,
Data.RAW_CONTACT_ID + "=" + rawContactId + " AND ("
- + MIMETYPE_SELECTION_IN_CLAUSE + ")",
+ + DataMimetypeQuery.MIMETYPE_SELECTION_IN_CLAUSE + ")",
null, null, null, null);
try {
while (c.moveToNext()) {
- String mimeType = c.getString(COL_MIMETYPE);
- String data1 = c.getString(COL_DATA1);
- String data2 = c.getString(COL_DATA2);
+ String mimeType = c.getString(DataMimetypeQuery.MIMETYPE);
+ String data = c.getString(DataMimetypeQuery.DATA1);
if (mimeType.equals(CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)) {
- addMatchCandidatesStructuredName(data1, data2, mode, candidates);
+ addMatchCandidatesStructuredName(data, candidates);
} else if (mimeType.equals(CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
- if (!TextUtils.isEmpty(data2)) {
- addMatchCandidatesEmail(data2, mode, candidates);
- lookupEmailMatches(db, data2, matcher);
+ if (!TextUtils.isEmpty(data)) {
+ addMatchCandidatesEmail(data, mode, candidates);
+ lookupEmailMatches(db, data, matcher);
}
} else if (mimeType.equals(CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
- if (!TextUtils.isEmpty(data2)) {
- lookupPhoneMatches(db, data2, matcher);
+ if (!TextUtils.isEmpty(data)) {
+ lookupPhoneMatches(db, data, matcher);
}
} else if (mimeType.equals(CommonDataKinds.Nickname.CONTENT_ITEM_TYPE)) {
- if (!TextUtils.isEmpty(data2)) {
- addMatchCandidatesNickname(data2, mode, candidates);
- lookupNicknameMatches(db, data2, matcher);
+ if (!TextUtils.isEmpty(data)) {
+ addMatchCandidatesNickname(data, mode, candidates);
+ lookupNicknameMatches(db, data, matcher);
}
}
}
@@ -840,65 +864,12 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
}
/**
- * Looks for matches based on the full name (first + last).
+ * Looks for matches based on the full name.
*/
- private void addMatchCandidatesStructuredName(String givenName, String familyName, int mode,
- MatchCandidateList candidates) {
- if (TextUtils.isEmpty(givenName)) {
-
- // If neither the first nor last name are specified, we won't aggregate
- if (TextUtils.isEmpty(familyName)) {
- return;
- }
-
- addMatchCandidatesSingleName(familyName, candidates);
- } else if (TextUtils.isEmpty(familyName)) {
- addMatchCandidatesSingleName(givenName, candidates);
- } else {
- addMatchCandidatesFullName(givenName, familyName, mode, candidates);
- }
- }
-
- private void addMatchCandidatesSingleName(String name, MatchCandidateList candidates) {
- String nameN = NameNormalizer.normalize(name);
- candidates.add(nameN, NameLookupType.NAME_EXACT);
- candidates.add(nameN, NameLookupType.NAME_COLLATION_KEY);
-
- // Take care of first and last names swapped
- String[] clusters = mOpenHelper.getCommonNicknameClusters(nameN);
- if (clusters != null) {
- for (int i = 0; i < clusters.length; i++) {
- candidates.add(clusters[i], NameLookupType.NAME_VARIANT);
- }
- }
- }
-
- private void addMatchCandidatesFullName(String givenName, String familyName, int mode,
- MatchCandidateList candidates) {
- final String givenNameN = NameNormalizer.normalize(givenName);
- final String[] givenNameNicknames = mOpenHelper.getCommonNicknameClusters(givenNameN);
- final String familyNameN = NameNormalizer.normalize(familyName);
- final String[] familyNameNicknames = mOpenHelper.getCommonNicknameClusters(familyNameN);
- candidates.add(givenNameN + "." + familyNameN, NameLookupType.NAME_EXACT);
- candidates.add(givenNameN + familyNameN, NameLookupType.NAME_COLLATION_KEY);
- candidates.add(familyNameN + givenNameN, NameLookupType.NAME_COLLATION_KEY);
- if (givenNameNicknames != null) {
- for (int i = 0; i < givenNameNicknames.length; i++) {
- candidates.add(givenNameNicknames[i] + "." + familyNameN,
- NameLookupType.NAME_VARIANT);
- candidates.add(familyNameN + "." + givenNameNicknames[i],
- NameLookupType.NAME_VARIANT);
- }
- }
- candidates.add(familyNameN + "." + givenNameN, NameLookupType.NAME_VARIANT);
- if (familyNameNicknames != null) {
- for (int i = 0; i < familyNameNicknames.length; i++) {
- candidates.add(familyNameNicknames[i] + "." + givenNameN,
- NameLookupType.NAME_VARIANT);
- candidates.add(givenNameN + "." + familyNameNicknames[i],
- NameLookupType.NAME_VARIANT);
- }
- }
+ private void addMatchCandidatesStructuredName(String name, MatchCandidateList candidates) {
+ AggregationNameLookupBuilder builder =
+ new AggregationNameLookupBuilder(mContactsProvider.getNameSplitter(), candidates);
+ builder.insertNameLookup(0, 0, name);
}
/**
@@ -984,14 +955,14 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator
*/
private void matchAllCandidates(SQLiteDatabase db, String selection,
MatchCandidateList candidates, ContactMatcher matcher, int algorithm) {
- final Cursor c = db.query(Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS, NAME_LOOKUP_COLUMNS,
+ final Cursor c = db.query(NameLookupQuery.TABLE, NameLookupQuery.COLUMNS,
selection, null, null, null, null, String.valueOf(PRIMARY_HIT_LIMIT));
try {
while (c.moveToNext()) {
- Long contactId = c.getLong(COL_NAME_LOOKUP_CONTACT_ID);
- String name = c.getString(COL_NORMALIZED_NAME);
- int nameType = c.getInt(COL_NAME_TYPE);
+ Long contactId = c.getLong(NameLookupQuery.CONTACT_ID);
+ String name = c.getString(NameLookupQuery.NORMALIZED_NAME);
+ int nameType = c.getInt(NameLookupQuery.NAME_TYPE);
// Determine which candidate produced this match
for (int i = 0; i < candidates.mCount; i++) {
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java
index 14ab008..f463990 100644
--- a/src/com/android/providers/contacts/ContactsProvider2.java
+++ b/src/com/android/providers/contacts/ContactsProvider2.java
@@ -28,6 +28,7 @@ import com.android.providers.contacts.OpenHelper.GroupsColumns;
import com.android.providers.contacts.OpenHelper.MimetypesColumns;
import com.android.providers.contacts.OpenHelper.NameLookupColumns;
import com.android.providers.contacts.OpenHelper.NameLookupType;
+import com.android.providers.contacts.OpenHelper.NicknameLookupColumns;
import com.android.providers.contacts.OpenHelper.PhoneColumns;
import com.android.providers.contacts.OpenHelper.PhoneLookupColumns;
import com.android.providers.contacts.OpenHelper.PresenceColumns;
@@ -92,12 +93,15 @@ import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.telephony.PhoneNumberUtils;
import android.text.TextUtils;
+import android.text.util.Rfc822Token;
+import android.text.util.Rfc822Tokenizer;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
+import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -235,13 +239,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
public static final String[] COLUMNS = new String[] {
MimetypesColumns.MIMETYPE,
Data.IS_PRIMARY,
- Data.DATA2,
+ Data.DATA1,
StructuredName.DISPLAY_NAME,
};
public static final int MIMETYPE = 0;
public static final int IS_PRIMARY = 1;
- public static final int DATA2 = 2;
+ public static final int DATA1 = 2;
public static final int DISPLAY_NAME = 3;
}
@@ -253,7 +257,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
MimetypesColumns.MIMETYPE,
Data.RAW_CONTACT_ID,
Data.IS_PRIMARY,
- Data.DATA2,
+ Data.DATA1,
};
public static final String[] COLUMNS = new String[] {
@@ -261,14 +265,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
MimetypesColumns.MIMETYPE,
Data.RAW_CONTACT_ID,
Data.IS_PRIMARY,
- Data.DATA2,
+ Data.DATA1,
};
public static final int _ID = 0;
public static final int MIMETYPE = 1;
public static final int RAW_CONTACT_ID = 2;
public static final int IS_PRIMARY = 3;
- public static final int DATA2 = 4;
+ public static final int DATA1 = 4;
}
private interface DataUpdateQuery {
@@ -279,6 +283,17 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
int MIMETYPE = 2;
}
+
+ private interface NicknameLookupQuery {
+ String TABLE = Tables.NICKNAME_LOOKUP;
+
+ String[] COLUMNS = new String[] {
+ NicknameLookupColumns.CLUSTER
+ };
+
+ int CLUSTER = 0;
+ }
+
private static final HashMap<String, Integer> sDisplayNameSources;
static {
sDisplayNameSources = new HashMap<String, Integer>();
@@ -341,6 +356,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private SQLiteStatement mAggregatedPresenceReplace;
/** Precompiled sql statement for updating an aggregated presence status */
private SQLiteStatement mAggregatedPresenceStatusUpdate;
+ private SQLiteStatement mNameLookupInsert;
+ private SQLiteStatement mNameLookupDelete;
static {
// Contacts URI matching table
@@ -784,7 +801,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
try {
while (c.moveToNext()) {
long dataId = c.getLong(DataDeleteQuery._ID);
- int type = c.getInt(DataDeleteQuery.DATA2);
+ int type = c.getInt(DataDeleteQuery.DATA1);
if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
primaryId = dataId;
primaryType = type;
@@ -827,7 +844,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
name = c.getString(DisplayNameQuery.DISPLAY_NAME);
primary = true;
} else {
- name = c.getString(DisplayNameQuery.DATA2);
+ name = c.getString(DisplayNameQuery.DATA1);
primary = (c.getInt(DisplayNameQuery.IS_PRIMARY) != 0);
}
@@ -901,10 +918,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
long dataId = super.insert(db, rawContactId, values);
- String givenName = values.getAsString(StructuredName.GIVEN_NAME);
- String familyName = values.getAsString(StructuredName.FAMILY_NAME);
- mOpenHelper.insertNameLookupForStructuredName(rawContactId, dataId, givenName,
- familyName);
+ String name = values.getAsString(StructuredName.DISPLAY_NAME);
+ insertNameLookupForStructuredName(rawContactId, dataId, name);
fixRawContactDisplayName(db, rawContactId);
return dataId;
}
@@ -920,14 +935,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
super.update(db, values, c, callerIsSyncAdapter);
- boolean hasGivenName = values.containsKey(StructuredName.GIVEN_NAME);
- boolean hasFamilyName = values.containsKey(StructuredName.FAMILY_NAME);
- if (hasGivenName || hasFamilyName) {
- String givenName = augmented.getAsString(StructuredName.GIVEN_NAME);
- String familyName = augmented.getAsString(StructuredName.FAMILY_NAME);
- mOpenHelper.deleteNameLookup(dataId);
- mOpenHelper.insertNameLookupForStructuredName(rawContactId, dataId, givenName,
- familyName);
+ if (values.containsKey(StructuredName.DISPLAY_NAME)) {
+ String name = values.getAsString(StructuredName.DISPLAY_NAME);
+ deleteNameLookup(dataId);
+ insertNameLookupForStructuredName(rawContactId, dataId, name);
}
fixRawContactDisplayName(db, rawContactId);
}
@@ -939,7 +950,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
int count = super.delete(db, c);
- mOpenHelper.deleteNameLookup(dataId);
+ deleteNameLookup(dataId);
fixRawContactDisplayName(db, rawContactId);
return count;
}
@@ -1138,7 +1149,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
long dataId = super.insert(db, rawContactId, values);
fixRawContactDisplayName(db, rawContactId);
- mOpenHelper.insertNameLookupForEmail(rawContactId, dataId, address);
+ insertNameLookupForEmail(rawContactId, dataId, address);
return dataId;
}
@@ -1151,8 +1162,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
super.update(db, values, c, callerIsSyncAdapter);
- mOpenHelper.deleteNameLookup(dataId);
- mOpenHelper.insertNameLookupForEmail(rawContactId, dataId, address);
+ deleteNameLookup(dataId);
+ insertNameLookupForEmail(rawContactId, dataId, address);
fixRawContactDisplayName(db, rawContactId);
}
@@ -1163,7 +1174,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
int count = super.delete(db, c);
- mOpenHelper.deleteNameLookup(dataId);
+ deleteNameLookup(dataId);
fixRawContactDisplayName(db, rawContactId);
return count;
}
@@ -1193,7 +1204,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
long dataId = super.insert(db, rawContactId, values);
fixRawContactDisplayName(db, rawContactId);
- mOpenHelper.insertNameLookupForNickname(rawContactId, dataId, nickname);
+ insertNameLookupForNickname(rawContactId, dataId, nickname);
return dataId;
}
@@ -1206,8 +1217,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
super.update(db, values, c, callerIsSyncAdapter);
- mOpenHelper.deleteNameLookup(dataId);
- mOpenHelper.insertNameLookupForNickname(rawContactId, dataId, nickname);
+ deleteNameLookup(dataId);
+ insertNameLookupForNickname(rawContactId, dataId, nickname);
fixRawContactDisplayName(db, rawContactId);
}
@@ -1218,7 +1229,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
int count = super.delete(db, c);
- mOpenHelper.deleteNameLookup(dataId);
+ deleteNameLookup(dataId);
fixRawContactDisplayName(db, rawContactId);
return count;
}
@@ -1415,6 +1426,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
private OpenHelper mOpenHelper;
private NameSplitter mNameSplitter;
+ private NameLookupBuilder mNameLookupBuilder;
+ private HashMap<String, SoftReference<String[]>> mNicknameClusterCache =
+ new HashMap<String, SoftReference<String[]>>();
private PostalSplitter mPostalSplitter;
private ContactAggregator mContactAggregator;
@@ -1509,8 +1523,16 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
context.getString(com.android.internal.R.string.common_name_suffixes),
context.getString(com.android.internal.R.string.common_name_conjunctions),
locale);
+ mNameLookupBuilder = new StructuredNameLookupBuilder(mNameSplitter);
mPostalSplitter = new PostalSplitter(locale);
+ mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
+ + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
+ + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
+ + ") VALUES (?,?,?,?)");
+ mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
+ + NameLookupColumns.DATA_ID + "=?");
+
mDataRowHandlers = new HashMap<String, DataRowHandler>();
mDataRowHandlers.put(Email.CONTENT_ITEM_TYPE, new EmailDataRowHandler());
@@ -4210,6 +4232,127 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun
mSetSuperPrimaryStatement.execute();
}
+ public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
+ if (TextUtils.isEmpty(email)) {
+ return;
+ }
+
+ Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
+ if (tokens.length == 0) {
+ return;
+ }
+
+ String address = tokens[0].getAddress();
+ int at = address.indexOf('@');
+ if (at != -1) {
+ address = address.substring(0, at);
+ }
+
+ insertNameLookup(rawContactId, dataId,
+ NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
+ }
+
+ /**
+ * Normalizes the nickname and inserts it in the name lookup table.
+ */
+ public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
+ if (TextUtils.isEmpty(nickname)) {
+ return;
+ }
+
+ insertNameLookup(rawContactId, dataId,
+ NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
+ }
+
+
+ public void insertNameLookupForStructuredName(long rawContactId, long dataId, String name) {
+ mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name);
+ }
+
+ /**
+ * Returns nickname cluster IDs or null. Maintains cache.
+ */
+ protected String[] getCommonNicknameClusters(String normalizedName) {
+ SoftReference<String[]> ref;
+ String[] clusters = null;
+ synchronized (mNicknameClusterCache) {
+ if (mNicknameClusterCache.containsKey(normalizedName)) {
+ ref = mNicknameClusterCache.get(normalizedName);
+ if (ref == null) {
+ return null;
+ }
+ clusters = ref.get();
+ }
+ }
+
+ if (clusters == null) {
+ clusters = loadNicknameClusters(normalizedName);
+ ref = clusters == null ? null : new SoftReference<String[]>(clusters);
+ synchronized (mNicknameClusterCache) {
+ mNicknameClusterCache.put(normalizedName, ref);
+ }
+ }
+ return clusters;
+ }
+
+ protected String[] loadNicknameClusters(String normalizedName) {
+ SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ String[] clusters = null;
+ Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS,
+ NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
+ null, null, null);
+ try {
+ int count = cursor.getCount();
+ if (count > 0) {
+ clusters = new String[count];
+ for (int i = 0; i < count; i++) {
+ cursor.moveToNext();
+ clusters[i] = cursor.getString(NicknameLookupQuery.CLUSTER);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+ return clusters;
+ }
+
+ private class StructuredNameLookupBuilder extends NameLookupBuilder {
+
+ public StructuredNameLookupBuilder(NameSplitter splitter) {
+ super(splitter);
+ }
+
+ @Override
+ protected void insertNameLookup(long rawContactId, long dataId, int lookupType,
+ String name) {
+ ContactsProvider2.this.insertNameLookup(rawContactId, dataId, lookupType, name);
+ }
+
+ @Override
+ protected String[] getCommonNicknameClusters(String normalizedName) {
+ return ContactsProvider2.this.getCommonNicknameClusters(normalizedName);
+ }
+ }
+
+ /**
+ * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
+ */
+ public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
+ DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
+ DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
+ DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
+ DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
+ mNameLookupInsert.executeInsert();
+ }
+
+ /**
+ * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
+ */
+ public void deleteNameLookup(long dataId) {
+ DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
+ mNameLookupDelete.execute();
+ }
+
private void appendContactFilterAsNestedQuery(StringBuilder sb, String filterParam) {
sb.append("(SELECT DISTINCT " + RawContacts.CONTACT_ID + " FROM " + Tables.RAW_CONTACTS
+ " JOIN name_lookup ON(" + RawContactsColumns.CONCRETE_ID + "=raw_contact_id)"
diff --git a/src/com/android/providers/contacts/GlobalSearchSupport.java b/src/com/android/providers/contacts/GlobalSearchSupport.java
index 3ae2a9a..a076d34 100644
--- a/src/com/android/providers/contacts/GlobalSearchSupport.java
+++ b/src/com/android/providers/contacts/GlobalSearchSupport.java
@@ -101,7 +101,9 @@ public class GlobalSearchSupport {
DataColumns.CONCRETE_ID + " AS data_id",
MimetypesColumns.MIMETYPE,
Data.IS_SUPER_PRIMARY,
- Data.DATA2,
+ Organization.COMPANY,
+ Email.DATA,
+ Phone.NUMBER,
Contacts.PHOTO_ID,
};
@@ -111,8 +113,10 @@ public class GlobalSearchSupport {
public static final int DATA_ID = 3;
public static final int MIMETYPE = 4;
public static final int IS_SUPER_PRIMARY = 5;
- public static final int DATA2 = 6;
- public static final int PHOTO_ID = 7;
+ public static final int ORGANIZATION = 6;
+ public static final int EMAIL = 7;
+ public static final int PHONE = 8;
+ public static final int PHOTO_ID = 9;
}
private static class SearchSuggestion {
@@ -330,15 +334,15 @@ public class GlobalSearchSupport {
suggestion.titleIsName = true;
} else if (Organization.CONTENT_ITEM_TYPE.equals(mimetype)) {
if (isSuperPrimary || suggestion.organization == null) {
- suggestion.organization = c.getString(SearchSuggestionQuery.DATA2);
+ suggestion.organization = c.getString(SearchSuggestionQuery.ORGANIZATION);
}
} else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) {
if (isSuperPrimary || suggestion.email == null) {
- suggestion.email = c.getString(SearchSuggestionQuery.DATA2);
+ suggestion.email = c.getString(SearchSuggestionQuery.EMAIL);
}
} else if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
if (isSuperPrimary || suggestion.phoneNumber == null) {
- suggestion.phoneNumber = c.getString(SearchSuggestionQuery.DATA2);
+ suggestion.phoneNumber = c.getString(SearchSuggestionQuery.PHONE);
}
}
diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java
index aac2460..a1eb139 100644
--- a/src/com/android/providers/contacts/LegacyApiSupport.java
+++ b/src/com/android/providers/contacts/LegacyApiSupport.java
@@ -165,7 +165,7 @@ public class LegacyApiSupport {
+ " THEN 'custom:'||" + Tables.DATA + "." + Im.CUSTOM_PROTOCOL
+ " ELSE 'pre:'||" + Tables.DATA + "." + Im.PROTOCOL
+ " END)"
- + " ELSE " + DataColumns.CONCRETE_DATA2
+ + " ELSE " + Tables.DATA + "." + Email.DATA
+ " END)";
private static final Uri LIVE_FOLDERS_CONTACTS_URI = Uri.withAppendedPath(
@@ -592,11 +592,11 @@ public class LegacyApiSupport {
+ " AS " + ContactMethods.KIND + ", " +
DataColumns.CONCRETE_IS_PRIMARY
+ " AS " + ContactMethods.ISPRIMARY + ", " +
- DataColumns.CONCRETE_DATA1
+ Tables.DATA + "." + Email.TYPE
+ " AS " + ContactMethods.TYPE + ", " +
CONTACT_METHOD_DATA_SQL
+ " AS " + ContactMethods.DATA + ", " +
- DataColumns.CONCRETE_DATA3
+ Tables.DATA + "." + Email.LABEL
+ " AS " + ContactMethods.LABEL + ", " +
DataColumns.CONCRETE_DATA14
+ " AS " + ContactMethods.AUX_DATA + ", " +
diff --git a/src/com/android/providers/contacts/LegacyContactImporter.java b/src/com/android/providers/contacts/LegacyContactImporter.java
index 633d6fb..458096d 100644
--- a/src/com/android/providers/contacts/LegacyContactImporter.java
+++ b/src/com/android/providers/contacts/LegacyContactImporter.java
@@ -539,13 +539,10 @@ public class LegacyContactImporter {
NameSplitter.Name splitName = new NameSplitter.Name();
mNameSplitter.split(splitName, name);
- String givenNames = splitName.getGivenNames();
- String familyName = splitName.getFamilyName();
-
bindString(insert, StructuredNameInsert.PREFIX, splitName.getPrefix());
- bindString(insert, StructuredNameInsert.GIVEN_NAME, givenNames);
+ bindString(insert, StructuredNameInsert.GIVEN_NAME, splitName.getGivenNames());
bindString(insert, StructuredNameInsert.MIDDLE_NAME, splitName.getMiddleName());
- bindString(insert, StructuredNameInsert.FAMILY_NAME, familyName);
+ bindString(insert, StructuredNameInsert.FAMILY_NAME, splitName.getFamilyName());
bindString(insert, StructuredNameInsert.SUFFIX, splitName.getSuffix());
if (mPhoneticNameAvailable) {
@@ -555,7 +552,7 @@ public class LegacyContactImporter {
long dataId = insert(insert);
- mOpenHelper.insertNameLookupForStructuredName(id, dataId, givenNames, familyName);
+ mContactsProvider.insertNameLookupForStructuredName(id, dataId, name);
}
private void insertNote(Cursor c, SQLiteStatement insert) {
@@ -750,7 +747,7 @@ public class LegacyContactImporter {
bindString(insert, EmailInsert.LABEL, c.getString(ContactMethodsQuery.LABEL));
long dataId = insert(insert);
- mOpenHelper.insertNameLookupForEmail(personId, dataId, email);
+ mContactsProvider.insertNameLookupForEmail(personId, dataId, email);
}
private void insertIm(Cursor c, SQLiteStatement insert) {
diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java
new file mode 100644
index 0000000..95909d0
--- /dev/null
+++ b/src/com/android/providers/contacts/NameLookupBuilder.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.providers.contacts;
+
+import com.android.providers.contacts.OpenHelper.NameLookupType;
+
+/**
+ * Given a full name, constructs all possible variants of the name.
+ */
+public abstract class NameLookupBuilder {
+
+ private final NameSplitter mSplitter;
+ private String[] mNormalizedNames = new String[NameSplitter.MAX_TOKENS];
+ private String[][] mNicknameClusters = new String[NameSplitter.MAX_TOKENS][];
+ private StringBuilder mStringBuilder1 = new StringBuilder();
+ private StringBuilder mStringBuilder2 = new StringBuilder();
+ private String[] mNames = new String[NameSplitter.MAX_TOKENS];
+
+ public NameLookupBuilder(NameSplitter splitter) {
+ mSplitter = splitter;
+ }
+
+ /**
+ * Inserts a name lookup record with the supplied column values.
+ */
+ protected abstract void insertNameLookup(long rawContactId, long dataId, int lookupType,
+ String string);
+
+ /**
+ * Returns common nickname cluster IDs for a given name. For example, it
+ * will return the same value for "Robert", "Bob" and "Rob". Some names belong to multiple
+ * clusters, e.g. Leo could be Leonard or Leopold.
+ *
+ * May return null.
+ *
+ * @param normalizedName A normalized first name, see {@link NameNormalizer#normalize}.
+ */
+ protected abstract String[] getCommonNicknameClusters(String normalizedName);
+
+ /**
+ * Inserts name lookup records for the given structured name.
+ */
+ public void insertNameLookup(long rawContactId, long dataId, String name) {
+ int tokenCount = mSplitter.tokenize(mNames, name);
+ if (tokenCount == 0) {
+ return;
+ }
+
+ for (int i = 0; i < tokenCount; i++) {
+ mNormalizedNames[i] = normalizeName(mNames[i]);
+ }
+
+ // Phase I: insert all variants not involving nickname clusters
+ for (int i = 0; i < tokenCount; i++) {
+ mNames[i] = mNormalizedNames[i];
+ mNicknameClusters[i] = getCommonNicknameClusters(mNames[i]);
+ }
+
+ insertNameVariants(rawContactId, dataId, 0, tokenCount, true, true);
+ insertNicknamePermutations(rawContactId, dataId, 0, tokenCount);
+ }
+
+ protected String normalizeName(String name) {
+ return NameNormalizer.normalize(name);
+ }
+
+ /**
+ * Inserts all name variants based on permutations of tokens between
+ * fromIndex and toIndex
+ *
+ * @param initiallyExact true if the name without permutations is the exact
+ * original name
+ * @param buildCollationKey true if a collation key makes sense for these
+ * permutations (false if at least one of the tokens is a
+ * nickname cluster key)
+ */
+ private void insertNameVariants(long rawContactId, long dataId, int fromIndex, int toIndex,
+ boolean initiallyExact, boolean buildCollationKey) {
+ if (fromIndex == toIndex) {
+ insertNameVariant(rawContactId, dataId, toIndex,
+ initiallyExact ? NameLookupType.NAME_EXACT : NameLookupType.NAME_VARIANT,
+ buildCollationKey);
+ return;
+ }
+
+ // Swap the first token with each other token (including itself, which is a no-op)
+ // and recursively insert all permutations for the remaining tokens
+ String firstToken = mNames[fromIndex];
+ for (int i = fromIndex; i < toIndex; i++) {
+ mNames[fromIndex] = mNames[i];
+ mNames[i] = firstToken;
+
+ insertNameVariants(rawContactId, dataId, fromIndex + 1, toIndex,
+ initiallyExact && i == fromIndex, buildCollationKey);
+
+ mNames[i] = mNames[fromIndex];
+ mNames[fromIndex] = firstToken;
+ }
+ }
+
+ /**
+ * Inserts a single name variant and optionally its collation key counterpart.
+ */
+ private void insertNameVariant(long rawContactId, long dataId, int tokenCount,
+ int lookupType, boolean buildCollationKey) {
+ mStringBuilder1.setLength(0);
+
+ for (int i = 0; i < tokenCount; i++) {
+ if (i != 0) {
+ mStringBuilder1.append('.');
+ }
+ mStringBuilder1.append(mNames[i]);
+ }
+
+ insertNameLookup(rawContactId, dataId, lookupType, mStringBuilder1.toString());
+
+ if (buildCollationKey) {
+ mStringBuilder2.setLength(0);
+
+ for (int i = 0; i < tokenCount; i++) {
+ mStringBuilder2.append(mNames[i]);
+ }
+
+ insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY,
+ mStringBuilder2.toString());
+ }
+ }
+
+ /**
+ * For all tokens that correspond to nickname clusters, substitutes each cluster key
+ * and inserts all permutations with that key.
+ */
+ private void insertNicknamePermutations(long rawContactId, long dataId, int fromIndex,
+ int tokenCount) {
+ for (int i = fromIndex; i < tokenCount; i++) {
+ String[] clusters = mNicknameClusters[i];
+ if (clusters != null) {
+ String token = mNames[i];
+ for (int j = 0; j < clusters.length; j++) {
+ mNames[i] = clusters[j];
+
+ // Insert all permutations with this nickname cluster
+ insertNameVariants(rawContactId, dataId, 0, tokenCount, false, false);
+
+ // Repeat recursively for other nickname clusters
+ insertNicknamePermutations(rawContactId, dataId, i + 1, tokenCount);
+ }
+ mNames[i] = token;
+ }
+ }
+ }
+}
diff --git a/src/com/android/providers/contacts/NameNormalizer.java b/src/com/android/providers/contacts/NameNormalizer.java
index eca40fc..f40a632 100644
--- a/src/com/android/providers/contacts/NameNormalizer.java
+++ b/src/com/android/providers/contacts/NameNormalizer.java
@@ -45,7 +45,7 @@ public class NameNormalizer {
* of names. It ignores non-letter characters and removes accents.
*/
public static String normalize(String name) {
- return Hex.encodeHex(sCompressingCollator.getSortKey(lettersOnly(name)), true);
+ return Hex.encodeHex(sCompressingCollator.getSortKey(lettersAndDigitsOnly(name)), true);
}
/**
@@ -53,7 +53,8 @@ public class NameNormalizer {
* of mixed case characters, accents and, if all else is equal, length.
*/
public static int compareComplexity(String name1, String name2) {
- int diff = sComplexityCollator.compare(lettersOnly(name1), lettersOnly(name2));
+ int diff = sComplexityCollator.compare(lettersAndDigitsOnly(name1),
+ lettersAndDigitsOnly(name2));
if (diff != 0) {
return diff;
}
@@ -64,12 +65,12 @@ public class NameNormalizer {
/**
* Returns a string containing just the letters from the original string.
*/
- private static String lettersOnly(String name) {
+ private static String lettersAndDigitsOnly(String name) {
char[] letters = name.toCharArray();
int length = 0;
for (int i = 0; i < letters.length; i++) {
final char c = letters[i];
- if (Character.isLetter(c)) {
+ if (Character.isLetterOrDigit(c)) {
letters[length++] = c;
}
}
diff --git a/src/com/android/providers/contacts/NameSplitter.java b/src/com/android/providers/contacts/NameSplitter.java
index 258df10..9ceb444 100644
--- a/src/com/android/providers/contacts/NameSplitter.java
+++ b/src/com/android/providers/contacts/NameSplitter.java
@@ -16,14 +16,13 @@
package com.android.providers.contacts;
import android.content.ContentValues;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.text.TextUtils;
import java.util.HashSet;
import java.util.Locale;
import java.util.StringTokenizer;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.text.TextUtils;
-
/**
* The purpose of this class is to split a full name into given names and last
* name. The logic only supports having a single last name. If the full name has
@@ -41,6 +40,8 @@ import android.text.TextUtils;
*/
public class NameSplitter {
+ public static final int MAX_TOKENS = 10;
+
private final HashSet<String> mPrefixesSet;
private final HashSet<String> mSuffixesSet;
private final int mMaxSuffixLength;
@@ -105,7 +106,6 @@ public class NameSplitter {
}
private static class NameTokenizer extends StringTokenizer {
- private static final int MAX_TOKENS = 10;
private final String[] mTokens;
private int mDotBitmask;
private int mStartPointer;
@@ -191,6 +191,33 @@ public class NameSplitter {
}
/**
+ * Parses a full name and returns components as a list of tokens.
+ */
+ public int tokenize(String[] tokens, String fullName) {
+ if (fullName == null) {
+ return 0;
+ }
+
+ NameTokenizer tokenizer = new NameTokenizer(fullName);
+
+ if (tokenizer.mStartPointer == tokenizer.mEndPointer) {
+ return 0;
+ }
+
+ String firstToken = tokenizer.mTokens[tokenizer.mStartPointer];
+ if (mPrefixesSet.contains(firstToken.toUpperCase())) {
+ tokenizer.mStartPointer++;
+ }
+ int count = 0;
+ for (int i = tokenizer.mStartPointer; i < tokenizer.mEndPointer; i++) {
+ tokens[count++] = tokenizer.mTokens[i];
+ }
+
+ return count;
+ }
+
+
+ /**
* Parses a full name and returns parsed components in the Name object.
*/
public void split(Name name, String fullName) {
diff --git a/src/com/android/providers/contacts/OpenHelper.java b/src/com/android/providers/contacts/OpenHelper.java
index e68b736..9ac205f 100644
--- a/src/com/android/providers/contacts/OpenHelper.java
+++ b/src/com/android/providers/contacts/OpenHelper.java
@@ -23,7 +23,6 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
@@ -46,9 +45,6 @@ import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.SocialContract.Activities;
import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.text.util.Rfc822Token;
-import android.text.util.Rfc822Tokenizer;
import android.util.Log;
import java.util.HashMap;
@@ -62,7 +58,7 @@ import java.util.HashMap;
/* package */ class OpenHelper extends SQLiteOpenHelper {
private static final String TAG = "OpenHelper";
- private static final int DATABASE_VERSION = 91;
+ private static final int DATABASE_VERSION = 93;
private static final String DATABASE_NAME = "contacts2.db";
private static final String DATABASE_PRESENCE = "presence_db";
@@ -430,12 +426,6 @@ import java.util.HashMap;
String CONTACT_ID = "presence_contact_id";
}
- private static final String[] NICKNAME_LOOKUP_COLUMNS = new String[] {
- NicknameLookupColumns.CLUSTER
- };
-
- private static final int COL_NICKNAME_LOOKUP_CLUSTER = 0;
-
/** In-memory cache of previously found mimetype mappings */
private final HashMap<String, Long> mMimetypeCache = new HashMap<String, Long>();
/** In-memory cache of previously found package name mappings */
@@ -452,15 +442,13 @@ import java.util.HashMap;
private SQLiteStatement mContactIdAndMarkAggregatedUpdate;
private SQLiteStatement mMimetypeInsert;
private SQLiteStatement mPackageInsert;
- private SQLiteStatement mNameLookupInsert;
- private SQLiteStatement mNameLookupDelete;
private SQLiteStatement mDataMimetypeQuery;
private SQLiteStatement mActivitiesMimetypeQuery;
private final Context mContext;
private final SyncStateContentProviderHelper mSyncState;
- private HashMap<String, String[]> mNicknameClusterCache;
+
/** Compiled statements for updating {@link Contacts#IN_VISIBLE_GROUP}. */
private SQLiteStatement mVisibleUpdate;
@@ -534,12 +522,6 @@ import java.util.HashMap;
mActivitiesMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE
+ " FROM " + Tables.ACTIVITIES_JOIN_MIMETYPES + " WHERE " + Tables.ACTIVITIES + "."
+ Activities._ID + "=?");
- mNameLookupInsert = db.compileStatement("INSERT OR IGNORE INTO " + Tables.NAME_LOOKUP + "("
- + NameLookupColumns.RAW_CONTACT_ID + "," + NameLookupColumns.DATA_ID + ","
- + NameLookupColumns.NAME_TYPE + "," + NameLookupColumns.NORMALIZED_NAME
- + ") VALUES (?,?,?,?)");
- mNameLookupDelete = db.compileStatement("DELETE FROM " + Tables.NAME_LOOKUP + " WHERE "
- + NameLookupColumns.DATA_ID + "=?");
// Compile statements for updating visibility
final String visibleUpdate = "UPDATE " + Tables.CONTACTS + " SET "
@@ -702,9 +684,9 @@ import java.util.HashMap;
/**
* For email lookup and similar queries.
*/
- db.execSQL("CREATE INDEX data_mimetype_data2_index ON " + Tables.DATA + " (" +
+ db.execSQL("CREATE INDEX data_mimetype_data1_index ON " + Tables.DATA + " (" +
DataColumns.MIMETYPE_ID + "," +
- Data.DATA2 +
+ Data.DATA1 +
");");
/**
@@ -964,7 +946,8 @@ import java.util.HashMap;
+ DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")"
+ " LEFT OUTER JOIN " + Tables.GROUPS + " ON ("
+ MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
- + "' AND " + GroupsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_DATA1 + ")");
+ + "' AND " + GroupsColumns.CONCRETE_ID + "="
+ + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")");
String dataColumns =
@@ -1036,7 +1019,8 @@ import java.util.HashMap;
+ DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")"
+ " LEFT OUTER JOIN " + Tables.GROUPS + " ON ("
+ MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
- + "' AND " + GroupsColumns.CONCRETE_ID + "=" + DataColumns.CONCRETE_DATA1 + ")"
+ + "' AND " + GroupsColumns.CONCRETE_ID + "="
+ + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")"
+ " LEFT OUTER JOIN " + Tables.CONTACTS + " ON ("
+ RawContacts.CONTACT_ID + "=" + Tables.CONTACTS + "." + Contacts._ID + ")";
@@ -1428,127 +1412,6 @@ import java.util.HashMap;
}
}
- /**
- * Deletes all {@link Tables#NAME_LOOKUP} table rows associated with the specified data element.
- */
- public void deleteNameLookup(long dataId) {
- getWritableDatabase();
- DatabaseUtils.bindObjectToProgram(mNameLookupDelete, 1, dataId);
- mNameLookupDelete.execute();
- }
-
- /**
- * Inserts a record in the {@link Tables#NAME_LOOKUP} table.
- */
- public void insertNameLookup(long rawContactId, long dataId, int lookupType, String name) {
- getWritableDatabase();
- DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 1, rawContactId);
- DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 2, dataId);
- DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 3, lookupType);
- DatabaseUtils.bindObjectToProgram(mNameLookupInsert, 4, name);
- mNameLookupInsert.executeInsert();
- }
-
-
- /**
- * Inserts name lookup records for the given structured name.
- */
- public void insertNameLookupForStructuredName(long rawContactId, long dataId,
- String givenName, String familyName) {
- if (TextUtils.isEmpty(givenName)) {
-
- // If neither the first nor last name are specified, nothing to insert
- if (TextUtils.isEmpty(familyName)) {
- return;
- }
-
- insertNameLookupForSingleName(rawContactId, dataId, familyName);
- } else if (TextUtils.isEmpty(familyName)) {
- insertNameLookupForSingleName(rawContactId, dataId, givenName);
- } else {
- insertNameLookupForFullName(rawContactId, dataId, givenName, familyName);
- }
- }
-
- private void insertNameLookupForSingleName(long rawContactId, long dataId, String name) {
- String nameN = NameNormalizer.normalize(name);
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_EXACT, nameN);
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_COLLATION_KEY, nameN);
-
- // Take care of first and last names swapped
- String[] clusters = getCommonNicknameClusters(nameN);
- if (clusters != null) {
- for (int i = 0; i < clusters.length; i++) {
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_VARIANT, clusters[i]);
- }
- }
- }
-
- private void insertNameLookupForFullName(long rawContactId, long dataId, String givenName,
- String familyName) {
- final String givenNameN = NameNormalizer.normalize(givenName);
- final String[] givenNameNicknames = getCommonNicknameClusters(givenNameN);
- final String familyNameN = NameNormalizer.normalize(familyName);
- final String[] familyNameNicknames = getCommonNicknameClusters(familyNameN);
- insertNameLookup(rawContactId, dataId,
- NameLookupType.NAME_EXACT, givenNameN + "." + familyNameN);
- insertNameLookup(rawContactId, dataId,
- NameLookupType.NAME_VARIANT, familyNameN + "." + givenNameN);
- insertNameLookup(rawContactId, dataId,
- NameLookupType.NAME_COLLATION_KEY, givenNameN + familyNameN);
- insertNameLookup(rawContactId, dataId,
- NameLookupType.NAME_COLLATION_KEY, familyNameN + givenNameN);
-
- if (givenNameNicknames != null) {
- for (int i = 0; i < givenNameNicknames.length; i++) {
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_VARIANT,
- givenNameNicknames[i] + "." + familyNameN);
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_VARIANT,
- familyNameN + "." + givenNameNicknames[i]);
- }
- }
- if (familyNameNicknames != null) {
- for (int i = 0; i < familyNameNicknames.length; i++) {
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_VARIANT,
- familyNameNicknames[i] + "." + givenNameN);
- insertNameLookup(rawContactId, dataId, NameLookupType.NAME_VARIANT,
- givenNameN + "." + familyNameNicknames[i]);
- }
- }
- }
-
- public void insertNameLookupForEmail(long rawContactId, long dataId, String email) {
- if (TextUtils.isEmpty(email)) {
- return;
- }
-
- Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(email);
- if (tokens.length == 0) {
- return;
- }
-
- String address = tokens[0].getAddress();
- int at = address.indexOf('@');
- if (at != -1) {
- address = address.substring(0, at);
- }
-
- insertNameLookup(rawContactId, dataId,
- NameLookupType.EMAIL_BASED_NICKNAME, NameNormalizer.normalize(address));
- }
-
- /**
- * Normalizes the nickname and inserts it in the name lookup table.
- */
- public void insertNameLookupForNickname(long rawContactId, long dataId, String nickname) {
- if (TextUtils.isEmpty(nickname)) {
- return;
- }
-
- insertNameLookup(rawContactId, dataId,
- NameLookupType.NICKNAME, NameNormalizer.normalize(nickname));
- }
-
public void buildPhoneLookupAndRawContactQuery(SQLiteQueryBuilder qb, String number) {
String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number);
qb.setTables(Tables.DATA_JOIN_RAW_CONTACTS +
@@ -1639,52 +1502,6 @@ import java.util.HashMap;
}
}
- /**
- * Returns common nickname cluster IDs for a given name. For example, it
- * will return the same value for "Robert", "Bob" and "Rob". Some names belong to multiple
- * clusters, e.g. Leo could be Leonard or Leopold.
- *
- * May return null.
- *
- * @param normalizedName A normalized first name, see {@link NameNormalizer#normalize}.
- */
- public String[] getCommonNicknameClusters(String normalizedName) {
- if (mNicknameClusterCache == null) {
- mNicknameClusterCache = new HashMap<String, String[]>();
- }
-
- synchronized (mNicknameClusterCache) {
- if (mNicknameClusterCache.containsKey(normalizedName)) {
- return mNicknameClusterCache.get(normalizedName);
- }
- }
-
- String[] clusters = null;
- SQLiteDatabase db = getReadableDatabase();
-
- Cursor cursor = db.query(Tables.NICKNAME_LOOKUP, NICKNAME_LOOKUP_COLUMNS,
- NicknameLookupColumns.NAME + "=?", new String[] { normalizedName },
- null, null, null);
- try {
- int count = cursor.getCount();
- if (count > 0) {
- clusters = new String[count];
- for (int i = 0; i < count; i++) {
- cursor.moveToNext();
- clusters[i] = cursor.getString(COL_NICKNAME_LOOKUP_CLUSTER);
- }
- }
- } finally {
- cursor.close();
- }
-
- synchronized (mNicknameClusterCache) {
- mNicknameClusterCache.put(normalizedName, clusters);
- }
-
- return clusters;
- }
-
public static void copyStringValue(ContentValues toValues, String toKey,
ContentValues fromValues, String fromKey) {
if (fromValues.containsKey(fromKey)) {