diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/providers/contacts/aggregation/ContactAggregator2.java | 128 | ||||
-rw-r--r-- | src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java | 35 |
2 files changed, 97 insertions, 66 deletions
diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java index d70e34a..9beb6c2 100644 --- a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java +++ b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java @@ -16,6 +16,9 @@ package com.android.providers.contacts.aggregation; +import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_PRIMARY; +import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_SECONDARY; +import static com.android.providers.contacts.aggregation.util.RawContactMatcher.SCORE_THRESHOLD_SUGGEST; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.provider.ContactsContract.AggregationExceptions; @@ -54,13 +57,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static com.android.providers.contacts.aggregation.util.RawContactMatcher - .SCORE_THRESHOLD_PRIMARY; -import static com.android.providers.contacts.aggregation.util.RawContactMatcher - .SCORE_THRESHOLD_SECONDARY; -import static com.android.providers.contacts.aggregation.util.RawContactMatcher - .SCORE_THRESHOLD_SUGGEST; - /** * ContactAggregator2 deals with aggregating contact information with sufficient matching data * points. E.g., two John Doe contacts with same phone numbers are presumed to be the same @@ -564,6 +560,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { try { while (c.moveToNext()) { final long rId = c.getLong(IdentityLookupMatchQuery.RAW_CONTACT_ID); + if (rId == rawContactId) { + continue; + } final long contactId = c.getLong(IdentityLookupMatchQuery.CONTACT_ID); final long accountId = c.getLong(IdentityLookupMatchQuery.ACCOUNT_ID); matcher.matchIdentity(rId, contactId, accountId); @@ -585,6 +584,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { try { while (c.moveToNext()) { long rId = c.getLong(NameLookupMatchQuery.RAW_CONTACT_ID); + if (rId == rawContactId) { + continue; + } long contactId = c.getLong(NameLookupMatchQuery.CONTACT_ID); long accountId = c.getLong(NameLookupMatchQuery.ACCOUNT_ID); String name = c.getString(NameLookupMatchQuery.NAME); @@ -612,6 +614,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { try { while (c.moveToNext()) { long rId = c.getLong(EmailLookupQuery.RAW_CONTACT_ID); + if (rId == rawContactId) { + continue; + } long contactId = c.getLong(EmailLookupQuery.CONTACT_ID); long accountId = c.getLong(EmailLookupQuery.ACCOUNT_ID); matcher.updateScoreWithEmailMatch(rId, contactId, accountId); @@ -666,6 +671,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { try { while (c.moveToNext()) { long rId = c.getLong(PhoneLookupQuery.RAW_CONTACT_ID); + if (rId == rawContactId) { + continue; + } long contactId = c.getLong(PhoneLookupQuery.CONTACT_ID); long accountId = c.getLong(PhoneLookupQuery.ACCOUNT_ID); matcher.updateScoreWithPhoneNumberMatch(rId, contactId, accountId); @@ -789,28 +797,6 @@ public class ContactAggregator2 extends AbstractContactAggregator { } } - private PhotoEntry getPhotoMetadata(SQLiteDatabase db, long photoFileId) { - if (photoFileId == 0) { - // Assume standard thumbnail size. Don't bother getting a file size for priority; - // we should fall back to photo priority resolver if all we have are thumbnails. - int thumbDim = mContactsProvider.getMaxThumbnailDim(); - return new PhotoEntry(thumbDim * thumbDim, 0); - } else { - Cursor c = db.query(Tables.PHOTO_FILES, PhotoFileQuery.COLUMNS, PhotoFiles._ID + "=?", - new String[]{String.valueOf(photoFileId)}, null, null, null); - try { - if (c.getCount() == 1) { - c.moveToFirst(); - int pixelCount = - c.getInt(PhotoFileQuery.HEIGHT) * c.getInt(PhotoFileQuery.WIDTH); - return new PhotoEntry(pixelCount, c.getInt(PhotoFileQuery.FILESIZE)); - } - } finally { - c.close(); - } - } - return new PhotoEntry(0, 0); - } /** * Finds contacts with data matches and returns a list of {@link MatchScore}'s in the * descending order of match score. @@ -866,12 +852,20 @@ public class ContactAggregator2 extends AbstractContactAggregator { */ private void updateMatchScores(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { + //update primary score updateMatchScoresBasedOnExceptions(db, rawContactId, matcher); - updateMatchScoresBasedOnIdentityMatch(db, rawContactId, matcher); updateMatchScoresBasedOnNameMatches(db, rawContactId, matcher); - updateMatchScoresBasedOnEmailMatches(db, rawContactId, matcher); - updateMatchScoresBasedOnPhoneMatches(db, rawContactId, matcher); - updateMatchScoresBasedOnSecondaryData(db, rawContactId, candidates, matcher); + // update scores only if the raw contact doesn't have structured name + if (rawContactWithoutName(db, rawContactId)) { + updateMatchScoresBasedOnIdentityMatch(db, rawContactId, matcher); + updateMatchScoresBasedOnEmailMatches(db, rawContactId, matcher); + updateMatchScoresBasedOnPhoneMatches(db, rawContactId, matcher); + final List<Long> secondaryRawContactIds = matcher.prepareSecondaryMatchCandidates(); + if (secondaryRawContactIds != null + && secondaryRawContactIds.size() <= SECONDARY_HIT_LIMIT) { + updateScoreForCandidatesWithoutName(db, secondaryRawContactIds, matcher); + } + } } private void updateMatchScoresForSuggestionsBasedOnDataMatches(SQLiteDatabase db, @@ -886,35 +880,53 @@ public class ContactAggregator2 extends AbstractContactAggregator { } } - /** - * Update scores for matches with secondary data matching but insufficient primary scores. - * This method loads structured names for all candidate contacts and recomputes match scores - * using approximate matching. - */ - private void updateMatchScoresBasedOnSecondaryData(SQLiteDatabase db, - long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { - final List<Long> secondaryRawContactIds = matcher.prepareSecondaryMatchCandidates(); - if (secondaryRawContactIds == null || secondaryRawContactIds.size() > SECONDARY_HIT_LIMIT) { - return; + private boolean rawContactWithoutName(SQLiteDatabase db, long rawContactId) { + String selection = RawContacts._ID + " =" + rawContactId; + final Cursor c = db.query(NullNameRawContactsIdsQuery.TABLE, + NullNameRawContactsIdsQuery.COLUMNS, selection, null, null, null, null); + + try { + if (c.moveToFirst()) { + return TextUtils.isEmpty(c.getString(NullNameRawContactsIdsQuery.NAME)); + } + } finally { + c.close(); } + return false; + } - loadNameMatchCandidates(db, rawContactId, candidates, true); + /** + * Update scores for matches with secondary data matching but no structured name. + */ + private void updateScoreForCandidatesWithoutName(SQLiteDatabase db, + List<Long> secondaryRawContactIds, RawContactMatcher matcher) { mSb.setLength(0); + mSb.append(RawContacts._ID).append(" IN ("); for (int i = 0; i < secondaryRawContactIds.size(); i++) { if (i != 0) { - mSb.append(','); + mSb.append(","); } mSb.append(secondaryRawContactIds.get(i)); } + mSb.append( ")"); + final Cursor c = db.query(NullNameRawContactsIdsQuery.TABLE, + NullNameRawContactsIdsQuery.COLUMNS, mSb.toString(), null, null, null, null); - // We only want to compare structured names to structured names - // at this stage, we need to ignore all other sources of name lookup data. - mSb.append(") AND " + STRUCTURED_NAME_BASED_LOOKUP_SQL); - - matchAllCandidates(db, mSb.toString(), candidates, matcher, - RawContactMatcher.MATCHING_ALGORITHM_CONSERVATIVE, null); + try { + while (c.moveToNext()) { + Long rId = c.getLong(NullNameRawContactsIdsQuery.RAW_CONTACT_ID); + Long contactId = c.getLong(NullNameRawContactsIdsQuery.CONTACT_ID); + Long accountId = c.getLong(NullNameRawContactsIdsQuery.ACCOUNT_ID); + String name = c.getString(NullNameRawContactsIdsQuery.NAME); + if (TextUtils.isEmpty(name)) { + matcher.matchNoName(rId, contactId, accountId); + } + } + } finally { + c.close(); + } } protected interface IdentityLookupMatchQuery { @@ -1026,4 +1038,18 @@ public class ContactAggregator2 extends AbstractContactAggregator { int ACCOUNT_ID = 2; } + protected interface NullNameRawContactsIdsQuery { + final String TABLE = Tables.RAW_CONTACTS + " LEFT OUTER JOIN " + Tables.NAME_LOOKUP + + " ON "+ RawContacts._ID + " = " + NameLookupColumns.RAW_CONTACT_ID + + " AND " + NameLookupColumns.NAME_TYPE + " = " + NameLookupType.NAME_EXACT; + + final String[] COLUMNS = new String[] { + RawContacts._ID, RawContacts.CONTACT_ID, RawContactsColumns.ACCOUNT_ID, + NameLookupColumns.NORMALIZED_NAME}; + + int RAW_CONTACT_ID = 0; + int CONTACT_ID = 1; + int ACCOUNT_ID = 2; + int NAME = 3; + } } diff --git a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java index 88aa226..f39ae96 100644 --- a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java +++ b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java @@ -30,12 +30,11 @@ import java.util.List; public class RawContactMatcher { private static final String TAG = "ContactMatcher"; - // Best possible match score - public static final int MAX_SCORE = 100; - // Suggest to aggregate contacts if their match score is equal or greater than this threshold public static final int SCORE_THRESHOLD_SUGGEST = 50; + public static final int SCORE_THRESHOLD_NO_NAME = 50; + // Automatically aggregate contacts if their match score is equal or greater than this threshold public static final int SCORE_THRESHOLD_PRIMARY = 70; @@ -49,6 +48,9 @@ public class RawContactMatcher { // Score for matching email addresses private static final int EMAIL_MATCH_SCORE = 71; + // Score for matching identity + private static final int IDENTITY_MATCH_SCORE = 71; + // Score for matching nickname private static final int NICKNAME_MATCH_SCORE = 71; @@ -180,13 +182,6 @@ public class RawContactMatcher { } /** - * Marks the contact as a full match, because we found an Identity match - */ - public void matchIdentity(long rawContactId, long contactId, long accountId) { - updatePrimaryScore(rawContactId, contactId, accountId, MAX_SCORE); - } - - /** * Checks if there is a match and updates the overall score for the * specified contact for a discovered match. The new score is determined * by the prior score, by the type of name we were looking for, the type @@ -244,6 +239,10 @@ public class RawContactMatcher { updatePrimaryScore(rawContactId, contactId, accountId, score); } + public void matchIdentity(long rawContactId, long contactId, long accountId) { + updateSecondaryScore(rawContactId, contactId, accountId, IDENTITY_MATCH_SCORE); + } + public void updateScoreWithPhoneNumberMatch(long rawContactId, long contactId, long accountId) { updateSecondaryScore(rawContactId, contactId, accountId, PHONE_MATCH_SCORE); } @@ -278,9 +277,9 @@ public class RawContactMatcher { mScoreCount = 0; } /** - * Returns a list of IDs for raw contacts that are matched on secondary data elements - * (phone number, email address, nickname). We still need to obtain the approximate - * primary score for those contacts to determine if any of them should be aggregated. + * Returns a list of IDs for raw contacts that are only matched on secondary data elements + * (phone number, email address, nickname, identity). We need to check if they are missing + * structured name or not to decide if they should be aggregated. * <p> * May return null. */ @@ -295,7 +294,7 @@ public class RawContactMatcher { if (score.getSecondaryScore() >= SCORE_THRESHOLD_PRIMARY) { if (rawContactIds == null) { - rawContactIds = new ArrayList<Long>(); + rawContactIds = new ArrayList<>(); } rawContactIds.add(score.getRawContactId()); } @@ -320,7 +319,9 @@ public class RawContactMatcher { continue; } - if (score.getPrimaryScore() >= SCORE_THRESHOLD_SECONDARY) { + if (score.getPrimaryScore() >= SCORE_THRESHOLD_PRIMARY || + (score.getPrimaryScore() == SCORE_THRESHOLD_NO_NAME && + score.getSecondaryScore() > SCORE_THRESHOLD_SECONDARY)) { matches.add(score); } } @@ -351,4 +352,8 @@ public class RawContactMatcher { public String toString() { return mScoreList.subList(0, mScoreCount).toString(); } + + public void matchNoName(Long rawContactId, Long contactId, Long accountId) { + updatePrimaryScore(rawContactId, contactId, accountId, SCORE_THRESHOLD_NO_NAME); + } } |