diff options
author | Zheng Fu <zhengfu@google.com> | 2015-05-26 21:27:07 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2015-05-26 21:27:08 +0000 |
commit | f60c48e2125898602214b8abca43b9fdbc942e40 (patch) | |
tree | 34ebd6fb82207f40229a33e22e662648c73eb4ad /src/com/android/providers/contacts | |
parent | d93008163a8a2a4a877506eacdc4c4b1b2ad840e (diff) | |
parent | 891e5b4c377258ff7d7259f2bab6cd6bff202aee (diff) | |
download | packages_providers_ContactsProvider-f60c48e2125898602214b8abca43b9fdbc942e40.tar.gz packages_providers_ContactsProvider-f60c48e2125898602214b8abca43b9fdbc942e40.tar.bz2 packages_providers_ContactsProvider-f60c48e2125898602214b8abca43b9fdbc942e40.zip |
Merge "Fix bugs in aggregator2." into mnc-dev
Diffstat (limited to 'src/com/android/providers/contacts')
3 files changed, 135 insertions, 24 deletions
diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 8fce6a61..c4da2e8f 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -1534,6 +1534,18 @@ public class ContactsProvider2 extends AbstractContactsProvider return true; } + @VisibleForTesting + public void setNewAggregatorForTest(boolean enabled) { + mContactAggregator = (enabled) + ? new ContactAggregator2(this, mContactsHelper, + createPhotoPriorityResolver(getContext()), mNameSplitter, mCommonNicknameCache) + : new ContactAggregator(this, mContactsHelper, + createPhotoPriorityResolver(getContext()), mNameSplitter, mCommonNicknameCache); + mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true)); + initDataRowHandlers(mDataRowHandlers, mContactsHelper, mContactAggregator, + mContactsPhotoStore); + } + // Updates the locale set to reflect a new system locale. private static LocaleSet updateLocaleSet(LocaleSet oldLocales, Locale newLocale) { final Locale prevLocale = oldLocales.getPrimaryLocale(); diff --git a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java index d0486097..f696950d 100644 --- a/src/com/android/providers/contacts/aggregation/ContactAggregator2.java +++ b/src/com/android/providers/contacts/aggregation/ContactAggregator2.java @@ -147,9 +147,14 @@ public class ContactAggregator2 extends AbstractContactAggregator { RawContactMatcher matcher = new RawContactMatcher(); RawContactMatchingCandidates matchingCandidates = new RawContactMatchingCandidates(); if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) { - // Find the set of matching candidates - matchingCandidates = findRawContactMatchingCandidates(db, rawContactId, candidates, - matcher); + // If this is a newly inserted contact or a visible contact, look for + // data matches. + if (currentContactId == 0 + || mDbHelper.isContactInDefaultDirectory(db, currentContactId)) { + // Find the set of matching candidates + matchingCandidates = findRawContactMatchingCandidates(db, rawContactId, candidates, + matcher); + } } else if (aggregationMode == RawContacts.AGGREGATION_MODE_DISABLED) { return; } @@ -223,11 +228,10 @@ public class ContactAggregator2 extends AbstractContactAggregator { */ private RawContactMatchingCandidates findRawContactMatchingCandidates(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { - updateMatchScoresForSuggestionsBasedOnDataMatches(db, rawContactId, candidates, + updateMatchScores(db, rawContactId, candidates, matcher); final RawContactMatchingCandidates matchingCandidates = new RawContactMatchingCandidates( - matcher.pickBestMatches(SCORE_THRESHOLD_SUGGEST)); - + matcher.pickBestMatches()); Set<Long> newIds = new HashSet<>(); newIds.addAll(matchingCandidates.getRawContactIdSet()); // Keep doing the following until no new raw contact candidate is found. @@ -236,9 +240,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { final Set<Long> tmpIdSet = new HashSet<>(); for (long rId : newIds) { final RawContactMatcher rMatcher = new RawContactMatcher(); - updateMatchScoresForSuggestionsBasedOnDataMatches(db, rId, new MatchCandidateList(), + updateMatchScores(db, rId, new MatchCandidateList(), rMatcher); - List<MatchScore> newMatches = rMatcher.pickBestMatches(SCORE_THRESHOLD_SUGGEST); + List<MatchScore> newMatches = rMatcher.pickBestMatches(); for (MatchScore newMatch : newMatches) { final long newRawContactId = newMatch.getRawContactId(); if (!matchingCandidates.getRawContactIdSet().contains(newRawContactId)) { @@ -260,12 +264,10 @@ public class ContactAggregator2 extends AbstractContactAggregator { */ private void clearSuperPrimarySetting(SQLiteDatabase db, String rawContactIds) { final String sql = - "SELECT d." + DataColumns.MIMETYPE_ID + ", count(DISTINCT r." + - RawContacts.CONTACT_ID + ") c FROM " + Tables.DATA + " d JOIN " + - Tables.RAW_CONTACTS + " r on d." + Data.RAW_CONTACT_ID + " = r." + - RawContacts._ID +" WHERE d." + Data.IS_SUPER_PRIMARY + " = 1 AND d." + - Data.RAW_CONTACT_ID + " IN (" + rawContactIds + ") group by d." + - DataColumns.MIMETYPE_ID + " having c > 1"; + "SELECT " + DataColumns.MIMETYPE_ID + ", count(1) c FROM " + + Tables.DATA +" WHERE " + Data.IS_SUPER_PRIMARY + " = 1 AND " + + Data.RAW_CONTACT_ID + " IN (" + rawContactIds + ") group by " + + DataColumns.MIMETYPE_ID + " HAVING c > 1"; // Find out which mime-types exist with is_super_primary=true on more then one contacts. int index = 0; @@ -294,13 +296,12 @@ public class ContactAggregator2 extends AbstractContactAggregator { // in both raw contact of rawContactId and raw contacts of contactId String superPrimaryUpdateSql = "UPDATE " + Tables.DATA + " SET " + Data.IS_SUPER_PRIMARY + "=0" + - " WHERE (" + Data.RAW_CONTACT_ID + - " IN (SELECT " + RawContacts._ID + " FROM " + Tables.RAW_CONTACTS + - " WHERE " + Data.RAW_CONTACT_ID + " IN (" + rawContactIds + ")"; + " WHERE " + Data.RAW_CONTACT_ID + + " IN (" + rawContactIds + ")"; mimeTypeCondition.append(')'); superPrimaryUpdateSql += mimeTypeCondition.toString(); - db.execSQL(superPrimaryUpdateSql, null); + db.execSQL(superPrimaryUpdateSql); } private String buildExceptionMatchingSql(String rawContactIdSet1, String rawContactIdSet2, @@ -357,6 +358,12 @@ public class ContactAggregator2 extends AbstractContactAggregator { (currentContactContentsCount == 0) || canBeReused(db, currentCidForRawContact, connectedRawContactIds)) { contactId = currentCidForRawContact; + for (Long connectedRawContactId : connectedRawContactIds) { + Long cid = matchingCandidates.getContactId(connectedRawContactId); + if (cid != null && cid != contactId) { + cidsNeedToBeUpdated.add(cid); + } + } } else if (currentCidForRawContact != 0){ cidsNeedToBeUpdated.add(currentCidForRawContact); } @@ -373,8 +380,9 @@ public class ContactAggregator2 extends AbstractContactAggregator { } } } - createContactForRawContacts(db, txContext, connectedRawContactIds, contactId); clearSuperPrimarySetting(db, TextUtils.join(",", connectedRawContactIds)); + createContactForRawContacts(db, txContext, connectedRawContactIds, contactId); + for (Long cid : cidsNeedToBeUpdated) { long currentRcCount = 0; if (cid != 0) { @@ -850,12 +858,13 @@ public class ContactAggregator2 extends AbstractContactAggregator { } /** - * Computes scores for contacts that have matching data rows. + * Computes suggestion scores for contacts that have matching data rows. + * Aggregation suggestion doesn't consider aggregation exceptions, but is purely based on the + * raw contacts information. */ private void updateMatchScoresForSuggestionsBasedOnDataMatches(SQLiteDatabase db, long rawContactId, MatchCandidateList candidates, RawContactMatcher matcher) { - updateMatchScoresBasedOnExceptions(db, rawContactId, matcher); updateMatchScoresBasedOnIdentityMatch(db, rawContactId, matcher); updateMatchScoresBasedOnNameMatches(db, rawContactId, matcher); updateMatchScoresBasedOnEmailMatches(db, rawContactId, matcher); @@ -864,6 +873,19 @@ public class ContactAggregator2 extends AbstractContactAggregator { lookupApproximateNameMatches(db, candidates, matcher); } + /** + * Computes scores for contacts that have matching data rows. + */ + private void updateMatchScores(SQLiteDatabase db, long rawContactId, + MatchCandidateList candidates, RawContactMatcher matcher) { + 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); + } + private void updateMatchScoresForSuggestionsBasedOnDataMatches(SQLiteDatabase db, MatchCandidateList candidates, RawContactMatcher matcher, ArrayList<AggregationSuggestionParameter> parameters) { @@ -875,4 +897,35 @@ public class ContactAggregator2 extends AbstractContactAggregator { // TODO: add support for other parameter kinds } } + + /** + * 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; + } + + loadNameMatchCandidates(db, rawContactId, candidates, true); + + mSb.setLength(0); + mSb.append(RawContacts._ID).append(" IN ("); + for (int i = 0; i < secondaryRawContactIds.size(); i++) { + if (i != 0) { + mSb.append(','); + } + mSb.append(secondaryRawContactIds.get(i)); + } + + // 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); + } } diff --git a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java index 1abcfa16..88aa2265 100644 --- a/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java +++ b/src/com/android/providers/contacts/aggregation/util/RawContactMatcher.java @@ -43,9 +43,6 @@ public class RawContactMatcher { // and there is a secondary match (phone number, email etc). public static final int SCORE_THRESHOLD_SECONDARY = 50; - // Score for missing data (as opposed to present data but a bad match) - private static final int NO_DATA_SCORE = -1; - // Score for matching phone numbers private static final int PHONE_MATCH_SCORE = 71; @@ -280,6 +277,55 @@ public class RawContactMatcher { mScores.clear(); 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. + * <p> + * May return null. + */ + public List<Long> prepareSecondaryMatchCandidates() { + ArrayList<Long> rawContactIds = null; + + for (int i = 0; i < mScoreCount; i++) { + MatchScore score = mScoreList.get(i); + if (score.isKeepOut() || score.getPrimaryScore() > SCORE_THRESHOLD_PRIMARY){ + continue; + } + + if (score.getSecondaryScore() >= SCORE_THRESHOLD_PRIMARY) { + if (rawContactIds == null) { + rawContactIds = new ArrayList<Long>(); + } + rawContactIds.add(score.getRawContactId()); + } + score.setPrimaryScore(0); + } + return rawContactIds; + } + + /** + * Returns the list of raw contact Ids with the match score over threshold. + */ + public List<MatchScore> pickBestMatches() { + final List<MatchScore> matches = new ArrayList<>(); + for (int i = 0; i < mScoreCount; i++) { + MatchScore score = mScoreList.get(i); + if (score.isKeepOut()) { + continue; + } + + if (score.isKeepIn()) { + matches.add(score); + continue; + } + + if (score.getPrimaryScore() >= SCORE_THRESHOLD_SECONDARY) { + matches.add(score); + } + } + return matches; + } /** * Returns matches in the order of descending score. |