AbstractContactAggregator.java revision e2e9ac275e487ce558579ee65ff8f122cf498b07
1b94bfa502569ce869d443353c174d02754d42a82Zheng Fu/*
2b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * Copyright (C) 2015 The Android Open Source Project
3b94bfa502569ce869d443353c174d02754d42a82Zheng Fu *
4b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * Licensed under the Apache License, Version 2.0 (the "License");
5b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * you may not use this file except in compliance with the License.
6b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * You may obtain a copy of the License at
7b94bfa502569ce869d443353c174d02754d42a82Zheng Fu *
8b94bfa502569ce869d443353c174d02754d42a82Zheng Fu *      http://www.apache.org/licenses/LICENSE-2.0
9b94bfa502569ce869d443353c174d02754d42a82Zheng Fu *
10b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * Unless required by applicable law or agreed to in writing, software
11b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * distributed under the License is distributed on an "AS IS" BASIS,
12b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * See the License for the specific language governing permissions and
14b94bfa502569ce869d443353c174d02754d42a82Zheng Fu * limitations under the License
15b94bfa502569ce869d443353c174d02754d42a82Zheng Fu */
16b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
17b94bfa502569ce869d443353c174d02754d42a82Zheng Fupackage com.android.providers.contacts.aggregation;
18b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
19b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.internal.annotations.VisibleForTesting;
20b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactLookupKey;
21b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper;
22b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
23b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns;
24b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns;
25b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
26b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
27b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
28b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
29b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
30b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
31b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
32b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.Tables;
33b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsDatabaseHelper.Views;
34b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ContactsProvider2;
35b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.NameLookupBuilder;
36b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.NameNormalizer;
37b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.NameSplitter;
38b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.PhotoPriorityResolver;
39b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.ReorderingCursorWrapper;
40b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.TransactionContext;
41b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.aggregation.util.CommonNicknameCache;
42b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.aggregation.util.ContactAggregatorHelper;
43b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.aggregation.util.ContactMatcher;
44aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fuimport com.android.providers.contacts.aggregation.util.MatchScore;
45b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.android.providers.contacts.util.Clock;
46b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.google.android.collect.Maps;
47b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport com.google.common.collect.HashMultimap;
48aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fuimport com.google.common.collect.Multimap;
49b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
5063a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.database.Cursor;
5163a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.database.DatabaseUtils;
5263a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.database.sqlite.SQLiteDatabase;
5363a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.database.sqlite.SQLiteQueryBuilder;
5463a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.database.sqlite.SQLiteStatement;
5563a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.net.Uri;
5663a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.AggregationExceptions;
5763a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.CommonDataKinds.Email;
5863a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.CommonDataKinds.Identity;
5963a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.CommonDataKinds.Phone;
6063a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.CommonDataKinds.Photo;
6163a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.Contacts;
6263a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.Data;
6363a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.DisplayNameSources;
6463a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.FullNameStyle;
6563a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.PhotoFiles;
6663a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.PinnedPositions;
6763a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.RawContacts;
6863a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.provider.ContactsContract.StatusUpdates;
6963a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.text.TextUtils;
7063a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.util.EventLog;
7163a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fuimport android.util.Log;
7263a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fu
73b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.ArrayList;
74b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.Collections;
75b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.HashMap;
76b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.HashSet;
77b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.Iterator;
78b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.List;
79b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.Locale;
80b94bfa502569ce869d443353c174d02754d42a82Zheng Fuimport java.util.Set;
81b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
82b94bfa502569ce869d443353c174d02754d42a82Zheng Fu/**
83aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu * Base class of contact aggregator and profile aggregator
84b94bfa502569ce869d443353c174d02754d42a82Zheng Fu */
85aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fupublic abstract class AbstractContactAggregator {
86b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
87aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final String TAG = "ContactAggregator";
88b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
89aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final boolean DEBUG_LOGGING = Log.isLoggable(TAG, Log.DEBUG);
90aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
91b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
92aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final String STRUCTURED_NAME_BASED_LOOKUP_SQL =
93b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            NameLookupColumns.NAME_TYPE + " IN ("
94b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + NameLookupType.NAME_EXACT + ","
95b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + NameLookupType.NAME_VARIANT + ","
96b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + NameLookupType.NAME_COLLATION_KEY + ")";
97b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
98b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
99b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
100b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * SQL statement that sets the {@link ContactsColumns#LAST_STATUS_UPDATE_ID} column
101b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * on the contact to point to the latest social status update.
102b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
103aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final String UPDATE_LAST_STATUS_UPDATE_ID_SQL =
104b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            "UPDATE " + Tables.CONTACTS +
105b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            " SET " + ContactsColumns.LAST_STATUS_UPDATE_ID + "=" +
106b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    "(SELECT " + DataColumns.CONCRETE_ID +
107b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " FROM " + Tables.STATUS_UPDATES +
108b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " JOIN " + Tables.DATA +
109b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    "   ON (" + StatusUpdatesColumns.DATA_ID + "="
110b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            + DataColumns.CONCRETE_ID + ")" +
111b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " JOIN " + Tables.RAW_CONTACTS +
112b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    "   ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "="
113b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            + RawContactsColumns.CONCRETE_ID + ")" +
114b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " WHERE " + RawContacts.CONTACT_ID + "=?" +
115b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC,"
116b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            + StatusUpdates.STATUS +
117b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    " LIMIT 1)" +
118b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            " WHERE " + ContactsColumns.CONCRETE_ID + "=?";
119b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
120b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // From system/core/logcat/event-log-tags
121b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // aggregator [time, count] will be logged for each aggregator cycle.
122b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // For the query (as opposed to the merge), count will be negative
1233a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    static final int LOG_SYNC_CONTACTS_AGGREGATION = 2747;
124b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
125b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // If we encounter more than this many contacts with matching names, aggregate only this many
126aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final int PRIMARY_HIT_LIMIT = 15;
127aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final String PRIMARY_HIT_LIMIT_STRING = String.valueOf(PRIMARY_HIT_LIMIT);
128b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
129b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // If we encounter more than this many contacts with matching phone number or email,
130b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // don't attempt to aggregate - this is likely an error or a shared corporate data element.
131aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final int SECONDARY_HIT_LIMIT = 20;
132aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final String SECONDARY_HIT_LIMIT_STRING = String.valueOf(SECONDARY_HIT_LIMIT);
133b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
134b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // If we encounter no less than this many raw contacts in the best matching contact during
135b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // aggregation, don't attempt to aggregate - this is likely an error or a shared corporate
136b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // data element.
137b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    @VisibleForTesting
138b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    static final int AGGREGATION_CONTACT_SIZE_LIMIT = 50;
139b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
140b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // If we encounter more than this many contacts with matching name during aggregation
141b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // suggestion lookup, ignore the remaining results.
142aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static final int FIRST_LETTER_SUGGESTION_HIT_LIMIT = 100;
143aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
144aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final ContactsProvider2 mContactsProvider;
145aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final ContactsDatabaseHelper mDbHelper;
146aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected PhotoPriorityResolver mPhotoPriorityResolver;
147aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final NameSplitter mNameSplitter;
148aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final CommonNicknameCache mCommonNicknameCache;
149aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
150aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected boolean mEnabled = true;
151aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
152aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    /**
153aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu     * Precompiled sql statement for setting an aggregated presence
154aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu     */
155aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mRawContactCountQuery;
156aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mAggregatedPresenceDelete;
157aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mAggregatedPresenceReplace;
158aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mPresenceContactIdUpdate;
159aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mMarkForAggregation;
160aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mPhotoIdUpdate;
161aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mDisplayNameUpdate;
162aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mLookupKeyUpdate;
163aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mStarredUpdate;
164e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang    protected SQLiteStatement mSendToVoicemailUpdate;
165aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mPinnedUpdate;
166aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mContactIdAndMarkAggregatedUpdate;
167aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mContactIdUpdate;
168aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mContactUpdate;
169aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mContactInsert;
170aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected SQLiteStatement mResetPinnedForRawContact;
171aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
172aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected HashMap<Long, Integer> mRawContactsMarkedForAggregation = Maps.newHashMap();
173aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
174aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected String[] mSelectionArgs1 = new String[1];
175aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected String[] mSelectionArgs2 = new String[2];
176aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
177aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected long mMimeTypeIdIdentity;
178aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected long mMimeTypeIdEmail;
179aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected long mMimeTypeIdPhoto;
180aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected long mMimeTypeIdPhone;
181aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected String mRawContactsQueryByRawContactId;
182aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected String mRawContactsQueryByContactId;
183aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected StringBuilder mSb = new StringBuilder();
184aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected MatchCandidateList mCandidates = new MatchCandidateList();
185aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected DisplayNameCandidate mDisplayNameCandidate = new DisplayNameCandidate();
186b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
187b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
188b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Parameter for the suggestion lookup query.
189b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
190b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    public static final class AggregationSuggestionParameter {
191b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public final String kind;
192b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public final String value;
193b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
194b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public AggregationSuggestionParameter(String kind, String value) {
195b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            this.kind = kind;
196b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            this.value = value;
197b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
198b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
199b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
200b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
201b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Captures a potential match for a given name. The matching algorithm
202b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * constructs a bunch of NameMatchCandidate objects for various potential matches
203b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * and then executes the search in bulk.
204b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
205aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static class NameMatchCandidate {
206b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String mName;
207b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int mLookupType;
208b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
209b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public NameMatchCandidate(String name, int nameLookupType) {
210b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mName = name;
211b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mLookupType = nameLookupType;
212b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
213b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
214b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
215b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
216b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * A list of {@link NameMatchCandidate} that keeps its elements even when the list is
217b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * truncated. This is done for optimization purposes to avoid excessive object allocation.
218b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
219aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static class MatchCandidateList {
220aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        protected final ArrayList<NameMatchCandidate> mList = new ArrayList<NameMatchCandidate>();
221aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        protected int mCount;
222b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
223b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        /**
224b94bfa502569ce869d443353c174d02754d42a82Zheng Fu         * Adds a {@link NameMatchCandidate} element or updates the next one if it already exists.
225b94bfa502569ce869d443353c174d02754d42a82Zheng Fu         */
226b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public void add(String name, int nameLookupType) {
227b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (mCount >= mList.size()) {
228b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mList.add(new NameMatchCandidate(name, nameLookupType));
229b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            } else {
230b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameMatchCandidate candidate = mList.get(mCount);
231b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                candidate.mName = name;
232b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                candidate.mLookupType = nameLookupType;
233b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
234b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mCount++;
235b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
236b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
237b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public void clear() {
238b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mCount = 0;
239b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
240b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
241b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public boolean isEmpty() {
242b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return mCount == 0;
243b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
244b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
245b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
246b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
247b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * A convenience class used in the algorithm that figures out which of available
248b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * display names to use for an aggregate contact.
249b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
250b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private static class DisplayNameCandidate {
251b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long rawContactId;
252b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String displayName;
253b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int displayNameSource;
254b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        boolean isNameSuperPrimary;
255b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        boolean writableAccount;
256b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
257b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public DisplayNameCandidate() {
258b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            clear();
259b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
260b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
261b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public void clear() {
262b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            rawContactId = -1;
263b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            displayName = null;
264b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            displayNameSource = DisplayNameSources.UNDEFINED;
265b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            isNameSuperPrimary = false;
266b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            writableAccount = false;
267b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
268b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
269b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
270b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
271b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Constructor.
272b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
273aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    public AbstractContactAggregator(ContactsProvider2 contactsProvider,
274b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            ContactsDatabaseHelper contactsDatabaseHelper,
275b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            PhotoPriorityResolver photoPriorityResolver, NameSplitter nameSplitter,
276b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            CommonNicknameCache commonNicknameCache) {
277b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactsProvider = contactsProvider;
278b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDbHelper = contactsDatabaseHelper;
279b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPhotoPriorityResolver = photoPriorityResolver;
280b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mNameSplitter = nameSplitter;
281b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mCommonNicknameCache = commonNicknameCache;
282b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
283b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        SQLiteDatabase db = mDbHelper.getReadableDatabase();
284b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
285b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Since we have no way of determining which custom status was set last,
286b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // we'll just pick one randomly.  We are using MAX as an approximation of randomness
287b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String replaceAggregatePresenceSql =
288b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "("
289b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + AggregatedPresenceColumns.CONTACT_ID + ", "
290b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + StatusUpdates.PRESENCE + ", "
291b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + StatusUpdates.CHAT_CAPABILITY + ")"
292b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " SELECT " + PresenceColumns.CONTACT_ID + ","
293b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + StatusUpdates.PRESENCE + ","
294b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + StatusUpdates.CHAT_CAPABILITY
295b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " FROM " + Tables.PRESENCE
296b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " WHERE "
297b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " (" + StatusUpdates.PRESENCE
298b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                +       " * 10 + " + StatusUpdates.CHAT_CAPABILITY + ")"
299b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " = (SELECT "
300b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + "MAX (" + StatusUpdates.PRESENCE
301b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                +       " * 10 + " + StatusUpdates.CHAT_CAPABILITY + ")"
302b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " FROM " + Tables.PRESENCE
303b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " WHERE " + PresenceColumns.CONTACT_ID
304b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + "=?)"
305b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + PresenceColumns.CONTACT_ID
306b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + "=?;";
307b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregatedPresenceReplace = db.compileStatement(replaceAggregatePresenceSql);
308b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
309b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactCountQuery = db.compileStatement(
310b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "SELECT COUNT(" + RawContacts._ID + ")" +
311b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.RAW_CONTACTS +
312b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts.CONTACT_ID + "=?"
313b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " AND " + RawContacts._ID + "<>?");
314b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
315b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregatedPresenceDelete = db.compileStatement(
316b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "DELETE FROM " + Tables.AGGREGATED_PRESENCE +
317b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + AggregatedPresenceColumns.CONTACT_ID + "=?");
318b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
319b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mMarkForAggregation = db.compileStatement(
320b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.RAW_CONTACTS +
321b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + RawContactsColumns.AGGREGATION_NEEDED + "=1" +
322b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts._ID + "=?"
323b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0");
324b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
325b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPhotoIdUpdate = db.compileStatement(
326b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.CONTACTS +
327b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + Contacts.PHOTO_ID + "=?," + Contacts.PHOTO_FILE_ID + "=? " +
328b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + Contacts._ID + "=?");
329b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
330b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDisplayNameUpdate = db.compileStatement(
331b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.CONTACTS +
332b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + Contacts.NAME_RAW_CONTACT_ID + "=? " +
333b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + Contacts._ID + "=?");
334b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
335b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mLookupKeyUpdate = db.compileStatement(
336b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.CONTACTS +
337b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + Contacts.LOOKUP_KEY + "=? " +
338b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + Contacts._ID + "=?");
339b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
340b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mStarredUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
341b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + Contacts.STARRED + "=(SELECT (CASE WHEN COUNT(" + RawContacts.STARRED
342b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + ")=0 THEN 0 ELSE 1 END) FROM " + Tables.RAW_CONTACTS + " WHERE "
343b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + " AND "
344b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + RawContacts.STARRED + "=1)" + " WHERE " + Contacts._ID + "=?");
345b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
346e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        mSendToVoicemailUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
347e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + Contacts.SEND_TO_VOICEMAIL + "=(CASE WHEN (SELECT COUNT( "
348e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + RawContacts.SEND_TO_VOICEMAIL + ") FROM " + Tables.RAW_CONTACTS
349e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + " WHERE " + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + " AND "
350e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + RawContacts.SEND_TO_VOICEMAIL + "=1) = (SELECT COUNT("
351e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + RawContacts.SEND_TO_VOICEMAIL + ") FROM " + Tables.RAW_CONTACTS + " WHERE "
352e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID
353e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang                + ") THEN 1 ELSE 0 END)" + " WHERE " + Contacts._ID + "=?");
354e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang
355b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPinnedUpdate = db.compileStatement("UPDATE " + Tables.CONTACTS + " SET "
356b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + Contacts.PINNED + " = IFNULL((SELECT MIN(" + RawContacts.PINNED + ") FROM "
357b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + Tables.RAW_CONTACTS + " WHERE " + RawContacts.CONTACT_ID + "="
358b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + ContactsColumns.CONCRETE_ID + " AND " + RawContacts.PINNED + ">"
359b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + PinnedPositions.UNPINNED + ")," + PinnedPositions.UNPINNED + ") "
360b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + "WHERE " + Contacts._ID + "=?");
361b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
362b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdAndMarkAggregatedUpdate = db.compileStatement(
363b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.RAW_CONTACTS +
364b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + RawContacts.CONTACT_ID + "=?, "
365b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.AGGREGATION_NEEDED + "=0" +
366b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts._ID + "=?");
367b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
368b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdUpdate = db.compileStatement(
369b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.RAW_CONTACTS +
370b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + RawContacts.CONTACT_ID + "=?" +
371b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts._ID + "=?");
372b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
373b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPresenceContactIdUpdate = db.compileStatement(
374b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.PRESENCE +
375b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + PresenceColumns.CONTACT_ID + "=?" +
376b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + PresenceColumns.RAW_CONTACT_ID + "=?");
377b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
378b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactUpdate = db.compileStatement(ContactReplaceSqlStatement.UPDATE_SQL);
379b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactInsert = db.compileStatement(ContactReplaceSqlStatement.INSERT_SQL);
380b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
381b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mResetPinnedForRawContact = db.compileStatement(
382b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.RAW_CONTACTS +
383b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + RawContacts.PINNED + "=" + PinnedPositions.UNPINNED +
384b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts._ID + "=?");
385b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
386b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mMimeTypeIdEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
387b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mMimeTypeIdIdentity = mDbHelper.getMimeTypeId(Identity.CONTENT_ITEM_TYPE);
388b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mMimeTypeIdPhoto = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
389b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mMimeTypeIdPhone = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE);
390b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
391b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Query used to retrieve data from raw contacts to populate the corresponding aggregate
392b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactsQueryByRawContactId = String.format(Locale.US,
393b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactsQuery.SQL_FORMAT_BY_RAW_CONTACT_ID,
394b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mDbHelper.getMimeTypeIdForStructuredName(), mMimeTypeIdPhoto, mMimeTypeIdPhone);
395b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
396b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactsQueryByContactId = String.format(Locale.US,
397b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactsQuery.SQL_FORMAT_BY_CONTACT_ID,
398b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mDbHelper.getMimeTypeIdForStructuredName(), mMimeTypeIdPhoto, mMimeTypeIdPhone);
399b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
400b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
4013a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void setEnabled(boolean enabled) {
402b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mEnabled = enabled;
403b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
404b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
4053a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final boolean isEnabled() {
406b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return mEnabled;
407b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
408b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
409aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface AggregationQuery {
410b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL =
411b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "SELECT " + RawContacts._ID + "," + RawContacts.CONTACT_ID +
412b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        ", " + RawContactsColumns.ACCOUNT_ID +
413b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.RAW_CONTACTS +
414b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts._ID + " IN(";
415b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
416b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int _ID = 0;
417b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CONTACT_ID = 1;
418b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_ID = 2;
419b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
420b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
421b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
422b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Aggregate all raw contacts that were marked for aggregation in the current transaction.
423b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Call just before committing the transaction.
424b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
4253a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    // Overridden by ProfileAggregator.
426b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    public void aggregateInTransaction(TransactionContext txContext, SQLiteDatabase db) {
427b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int markedCount = mRawContactsMarkedForAggregation.size();
428b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (markedCount == 0) {
429b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
430b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
431b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
432b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long start = System.currentTimeMillis();
433b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (DEBUG_LOGGING) {
434b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Log.d(TAG, "aggregateInTransaction for " + markedCount + " contacts");
435b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
436b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
437b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        EventLog.writeEvent(LOG_SYNC_CONTACTS_AGGREGATION, start, -markedCount);
438b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
439b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int index = 0;
440b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
441b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // We don't use the cached string builder (namely mSb)  here, as this string can be very
442b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // long when upgrading (where we re-aggregate all visible contacts) and StringBuilder won't
443b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // shrink the internal storage.
444b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Note: don't use selection args here.  We just include all IDs directly in the selection,
445b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // because there's a limit for the number of parameters in a query.
446b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final StringBuilder sbQuery = new StringBuilder();
447b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sbQuery.append(AggregationQuery.SQL);
448b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (long rawContactId : mRawContactsMarkedForAggregation.keySet()) {
449b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (index > 0) {
450b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                sbQuery.append(',');
451b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
452b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            sbQuery.append(rawContactId);
453b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            index++;
454b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
455b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
456b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sbQuery.append(')');
457b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
458b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long[] rawContactIds;
459b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long[] contactIds;
460b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long[] accountIds;
461b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int actualCount;
462b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.rawQuery(sbQuery.toString(), null);
463b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
464b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            actualCount = c.getCount();
465b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            rawContactIds = new long[actualCount];
466b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            contactIds = new long[actualCount];
467b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            accountIds = new long[actualCount];
468b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
469b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            index = 0;
470b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
471b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                rawContactIds[index] = c.getLong(AggregationQuery._ID);
472b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactIds[index] = c.getLong(AggregationQuery.CONTACT_ID);
473b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                accountIds[index] = c.getLong(AggregationQuery.ACCOUNT_ID);
474b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                index++;
475b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
476b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
477b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
478b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
479b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
480b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (DEBUG_LOGGING) {
481b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Log.d(TAG, "aggregateInTransaction: initial query done.");
482b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
483b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
484b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (int i = 0; i < actualCount; i++) {
485b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            aggregateContact(txContext, db, rawContactIds[i], accountIds[i], contactIds[i],
486aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                    mCandidates);
487b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
488b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
489b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long elapsedTime = System.currentTimeMillis() - start;
490b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        EventLog.writeEvent(LOG_SYNC_CONTACTS_AGGREGATION, elapsedTime, actualCount);
491b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
492b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (DEBUG_LOGGING) {
493b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Log.d(TAG, "Contact aggregation complete: " + actualCount +
494b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    (actualCount == 0 ? "" : ", " + (elapsedTime / actualCount)
495b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            + " ms per raw contact"));
496b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
497b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
498b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
499b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    @SuppressWarnings("deprecation")
5003a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void triggerAggregation(TransactionContext txContext, long rawContactId) {
501b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (!mEnabled) {
502b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
503b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
504b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
505b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int aggregationMode = mDbHelper.getAggregationMode(rawContactId);
506b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        switch (aggregationMode) {
507b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            case RawContacts.AGGREGATION_MODE_DISABLED:
508b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                break;
509b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
510b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            case RawContacts.AGGREGATION_MODE_DEFAULT: {
511b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                markForAggregation(rawContactId, aggregationMode, false);
512b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                break;
513b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
514b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
515b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            case RawContacts.AGGREGATION_MODE_SUSPENDED: {
516b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long contactId = mDbHelper.getContactId(rawContactId);
517b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
518b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (contactId != 0) {
519b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    updateAggregateData(txContext, contactId);
520b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
521b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                break;
522b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
523b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
524b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            case RawContacts.AGGREGATION_MODE_IMMEDIATE: {
525b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                aggregateContact(txContext, mDbHelper.getWritableDatabase(), rawContactId);
526b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                break;
527b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
528b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
529b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
530b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
5313a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void clearPendingAggregations() {
532b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // HashMap woulnd't shrink the internal table once expands it, so let's just re-create
533b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // a new one instead of clear()ing it.
534b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactsMarkedForAggregation = Maps.newHashMap();
535b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
536b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
5373a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void markNewForAggregation(long rawContactId, int aggregationMode) {
538b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactsMarkedForAggregation.put(rawContactId, aggregationMode);
539b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
540b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
5413a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void markForAggregation(long rawContactId, int aggregationMode, boolean force) {
542b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int effectiveAggregationMode;
543b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (!force && mRawContactsMarkedForAggregation.containsKey(rawContactId)) {
544b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // As per ContactsContract documentation, default aggregation mode
545b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // does not override a previously set mode
546b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) {
547b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                effectiveAggregationMode = mRawContactsMarkedForAggregation.get(rawContactId);
548b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            } else {
549b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                effectiveAggregationMode = aggregationMode;
550b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
551b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
552b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mMarkForAggregation.bindLong(1, rawContactId);
553b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mMarkForAggregation.execute();
554b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            effectiveAggregationMode = aggregationMode;
555b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
556b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
557b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mRawContactsMarkedForAggregation.put(rawContactId, effectiveAggregationMode);
558b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
559b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
560b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private static class RawContactIdAndAggregationModeQuery {
561b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String TABLE = Tables.RAW_CONTACTS;
562b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
563aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        public static final String[] COLUMNS = {RawContacts._ID, RawContacts.AGGREGATION_MODE};
564b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
565b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String SELECTION = RawContacts.CONTACT_ID + "=?";
566b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
567b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final int _ID = 0;
568b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final int AGGREGATION_MODE = 1;
569b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
570b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
571b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
572b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Marks all constituent raw contacts of an aggregated contact for re-aggregation.
573b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
5743a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void markContactForAggregation(SQLiteDatabase db, long contactId) {
575b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(contactId);
576b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor cursor = db.query(RawContactIdAndAggregationModeQuery.TABLE,
577b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactIdAndAggregationModeQuery.COLUMNS,
578b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactIdAndAggregationModeQuery.SELECTION, mSelectionArgs1, null, null, null);
579b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
580b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (cursor.moveToFirst()) {
581b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId = cursor.getLong(RawContactIdAndAggregationModeQuery._ID);
582b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int aggregationMode = cursor.getInt(
583b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        RawContactIdAndAggregationModeQuery.AGGREGATION_MODE);
584b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // Don't re-aggregate AGGREGATION_MODE_SUSPENDED / AGGREGATION_MODE_DISABLED.
585b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // (Also just ignore deprecated AGGREGATION_MODE_IMMEDIATE)
586b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (aggregationMode == RawContacts.AGGREGATION_MODE_DEFAULT) {
587b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    markForAggregation(rawContactId, aggregationMode, true);
588b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
589b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
590b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
591b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.close();
592b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
593b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
594b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
595b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
596b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Mark all visible contacts for re-aggregation.
597b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     *
598b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * - Set {@link RawContactsColumns#AGGREGATION_NEEDED} For all visible raw_contacts with
599b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     *   {@link RawContacts#AGGREGATION_MODE_DEFAULT}.
600b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * - Also put them into {@link #mRawContactsMarkedForAggregation}.
601b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
6023a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final int markAllVisibleForAggregation(SQLiteDatabase db) {
603b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long start = System.currentTimeMillis();
604b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
605b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Set AGGREGATION_NEEDED for all visible raw_cotnacts with AGGREGATION_MODE_DEFAULT.
606b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // (Don't re-aggregate AGGREGATION_MODE_SUSPENDED / AGGREGATION_MODE_DISABLED)
607b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        db.execSQL("UPDATE " + Tables.RAW_CONTACTS + " SET " +
608b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactsColumns.AGGREGATION_NEEDED + "=1" +
609b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY +
610b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND " + RawContacts.AGGREGATION_MODE + "=" + RawContacts.AGGREGATION_MODE_DEFAULT
611b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                );
612b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
613b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int count;
614b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor cursor = db.rawQuery("SELECT " + RawContacts._ID +
615b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.RAW_CONTACTS +
616cc9ce6b1c8f9d7c4d8b4da96003ce74ccaa636ccZheng Fu                " WHERE " + RawContactsColumns.AGGREGATION_NEEDED + "=1 AND " +
617cc9ce6b1c8f9d7c4d8b4da96003ce74ccaa636ccZheng Fu                RawContacts.DELETED + "=0", null);
618b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
619b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            count = cursor.getCount();
620b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.moveToPosition(-1);
621b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (cursor.moveToNext()) {
622b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                final long rawContactId = cursor.getLong(0);
623b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mRawContactsMarkedForAggregation.put(rawContactId,
624b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        RawContacts.AGGREGATION_MODE_DEFAULT);
625b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
626b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
627b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.close();
628b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
629b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
630b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final long end = System.currentTimeMillis();
631b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Log.i(TAG, "Marked all visible contacts for aggregation: " + count + " raw contacts, " +
632b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                (end - start) + " ms");
633b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return count;
634b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
635b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
636b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
637b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Creates a new contact based on the given raw contact.  Does not perform aggregation.  Returns
638b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * the ID of the contact that was created.
639b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
6403a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    // Overridden by ProfileAggregator.
641b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    public long onRawContactInsert(
642b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            TransactionContext txContext, SQLiteDatabase db, long rawContactId) {
643b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = insertContact(db, rawContactId);
644b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        setContactId(rawContactId, contactId);
645b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDbHelper.updateContactVisible(txContext, contactId);
646b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return contactId;
647b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
648b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
6493a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final long insertContact(SQLiteDatabase db, long rawContactId) {
650b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(rawContactId);
651b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        computeAggregateData(db, mRawContactsQueryByRawContactId, mSelectionArgs1, mContactInsert);
652b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return mContactInsert.executeInsert();
653b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
654b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
655b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private static final class RawContactIdAndAccountQuery {
656b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String TABLE = Tables.RAW_CONTACTS;
657b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
658b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String[] COLUMNS = {
659b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContacts.CONTACT_ID,
660b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactsColumns.ACCOUNT_ID
661b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
662b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
663b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String SELECTION = RawContacts._ID + "=?";
664b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
665b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final int CONTACT_ID = 0;
666b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final int ACCOUNT_ID = 1;
667b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
668b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
6693a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    // Overridden by ProfileAggregator.
670b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    public void aggregateContact(
671b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            TransactionContext txContext, SQLiteDatabase db, long rawContactId) {
672b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (!mEnabled) {
673b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
674b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
675b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
676b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        MatchCandidateList candidates = new MatchCandidateList();
677b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
678b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = 0;
679b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long accountId = 0;
680b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(rawContactId);
681b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor cursor = db.query(RawContactIdAndAccountQuery.TABLE,
682b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactIdAndAccountQuery.COLUMNS, RawContactIdAndAccountQuery.SELECTION,
683b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mSelectionArgs1, null, null, null);
684b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
685b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (cursor.moveToFirst()) {
686b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactId = cursor.getLong(RawContactIdAndAccountQuery.CONTACT_ID);
687b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                accountId = cursor.getLong(RawContactIdAndAccountQuery.ACCOUNT_ID);
688b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
689b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
690b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.close();
691b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
692b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
693b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        aggregateContact(txContext, db, rawContactId, accountId, contactId,
694aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                candidates);
695b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
696b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
697b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    public void updateAggregateData(TransactionContext txContext, long contactId) {
698b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (!mEnabled) {
699b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
700b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
701b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
702b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final SQLiteDatabase db = mDbHelper.getWritableDatabase();
703b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        computeAggregateData(db, contactId, mContactUpdate);
704b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactUpdate.bindLong(ContactReplaceSqlStatement.CONTACT_ID, contactId);
705b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactUpdate.execute();
706b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
707b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDbHelper.updateContactVisible(txContext, contactId);
708b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        updateAggregatedStatusUpdate(contactId);
709b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
710b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
7113a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void updateAggregatedStatusUpdate(long contactId) {
712b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregatedPresenceReplace.bindLong(1, contactId);
713b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregatedPresenceReplace.bindLong(2, contactId);
714b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregatedPresenceReplace.execute();
715b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        updateLastStatusUpdateId(contactId);
716b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
717b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
718b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
719b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Adjusts the reference to the latest status update for the specified contact.
720b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
7213a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateLastStatusUpdateId(long contactId) {
722b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String contactIdString = String.valueOf(contactId);
723b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDbHelper.getWritableDatabase().execSQL(UPDATE_LAST_STATUS_UPDATE_ID_SQL,
724b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                new String[]{contactIdString, contactIdString});
725b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
726b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
727b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
728b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Given a specific raw contact, finds all matching aggregate contacts and chooses the one
729b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * with the highest match score.  If no such contact is found, creates a new contact.
730b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
731aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    abstract void aggregateContact(TransactionContext txContext, SQLiteDatabase db,
732aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu            long rawContactId, long accountId, long currentContactId,
733aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu            MatchCandidateList candidates);
734b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
735b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
736aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface RawContactMatchingSelectionStatement {
737aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        String SELECT_COUNT = "SELECT count(*) ";
738aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        String SELECT_ID = "SELECT d1." + Data.RAW_CONTACT_ID + ",d2." + Data.RAW_CONTACT_ID;
739b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
740b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
741b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
742b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Build sql to check if there is any identity match/mis-match between two sets of raw contact
743b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * ids on the same namespace.
744b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
7453a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final String buildIdentityMatchingSql(String rawContactIdSet1,
7463a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki            String rawContactIdSet2, boolean isIdentityMatching, boolean countOnly) {
747b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String identityType = String.valueOf(mMimeTypeIdIdentity);
748b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String matchingOperator = (isIdentityMatching) ? "=" : "!=";
749b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String sql =
750b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.DATA + " AS d1" +
751b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.DATA + " AS d2" +
752b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " ON (d1." + Identity.IDENTITY + matchingOperator +
753b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " d2." + Identity.IDENTITY + " AND" +
754b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " d1." + Identity.NAMESPACE + " = d2." + Identity.NAMESPACE + " )" +
755b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE d1." + DataColumns.MIMETYPE_ID + " = " + identityType +
756b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + DataColumns.MIMETYPE_ID + " = " + identityType +
757b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d1." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet1 + ")" +
758b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet2 + ")";
759b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return (countOnly) ? RawContactMatchingSelectionStatement.SELECT_COUNT + sql :
760b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactMatchingSelectionStatement.SELECT_ID + sql;
761b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
762b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
7633a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final String buildEmailMatchingSql(String rawContactIdSet1, String rawContactIdSet2,
764b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            boolean countOnly) {
765b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String emailType = String.valueOf(mMimeTypeIdEmail);
766b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String sql =
767b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.DATA + " AS d1" +
768b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.DATA + " AS d2" +
769d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu                        " ON d1." + Email.ADDRESS + "= d2." + Email.ADDRESS +
770b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE d1." + DataColumns.MIMETYPE_ID + " = " + emailType +
771b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + DataColumns.MIMETYPE_ID + " = " + emailType +
772b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d1." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet1 + ")" +
773b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet2 + ")";
774b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return (countOnly) ? RawContactMatchingSelectionStatement.SELECT_COUNT + sql :
775b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactMatchingSelectionStatement.SELECT_ID + sql;
776b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
777b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
7783a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final String buildPhoneMatchingSql(String rawContactIdSet1, String rawContactIdSet2,
779b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            boolean countOnly) {
780b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // It's a bit tricker because it has to be consistent with
781b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // updateMatchScoresBasedOnPhoneMatches().
782b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String phoneType = String.valueOf(mMimeTypeIdPhone);
783b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String sql =
784b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.PHONE_LOOKUP + " AS p1" +
785b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.DATA + " AS d1 ON " +
786b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        "(d1." + Data._ID + "=p1." + PhoneLookupColumns.DATA_ID + ")" +
787b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.PHONE_LOOKUP + " AS p2 ON (p1." + PhoneLookupColumns.MIN_MATCH +
788b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        "=p2." + PhoneLookupColumns.MIN_MATCH + ")" +
789b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.DATA + " AS d2 ON " +
790b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        "(d2." + Data._ID + "=p2." + PhoneLookupColumns.DATA_ID + ")" +
791b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE d1." + DataColumns.MIMETYPE_ID + " = " + phoneType +
792b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + DataColumns.MIMETYPE_ID + " = " + phoneType +
793b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d1." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet1 + ")" +
794b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND d2." + Data.RAW_CONTACT_ID + " IN (" + rawContactIdSet2 + ")" +
795b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND PHONE_NUMBERS_EQUAL(d1." + Phone.NUMBER + ",d2." + Phone.NUMBER + "," +
796b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        String.valueOf(mDbHelper.getUseStrictPhoneNumberComparisonParameter()) +
797b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        ")";
798b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return (countOnly) ? RawContactMatchingSelectionStatement.SELECT_COUNT + sql :
799b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContactMatchingSelectionStatement.SELECT_ID + sql;
800b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
801b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
8023a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final String buildExceptionMatchingSql(String rawContactIdSet1,
8033a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki            String rawContactIdSet2) {
804b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return "SELECT " + AggregationExceptions.RAW_CONTACT_ID1 + ", " +
805b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                AggregationExceptions.RAW_CONTACT_ID2 +
806b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.AGGREGATION_EXCEPTIONS +
807b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + AggregationExceptions.RAW_CONTACT_ID1 + " IN (" +
808aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                rawContactIdSet1 + ")" +
809b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND " + AggregationExceptions.RAW_CONTACT_ID2 + " IN (" + rawContactIdSet2 + ")" +
810b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " AND " + AggregationExceptions.TYPE + "=" +
811aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.TYPE_KEEP_TOGETHER ;
812b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
813b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
8143a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final boolean isFirstColumnGreaterThanZero(SQLiteDatabase db, String query) {
815b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return DatabaseUtils.longForQuery(db, query, null) > 0;
816b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
817b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
818b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
819b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Partition the given raw contact Ids to connected component based on aggregation exception,
820b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * identity matching, email matching or phone matching.
821b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
8223a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final Set<Set<Long>> findConnectedRawContacts(SQLiteDatabase db, Set<Long>
823aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu            rawContactIdSet) {
824b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Connections between two raw contacts
825aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        final Multimap<Long, Long> matchingRawIdPairs = HashMultimap.create();
826b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String rawContactIds = TextUtils.join(",", rawContactIdSet);
827b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        findIdPairs(db, buildExceptionMatchingSql(rawContactIds, rawContactIds),
828b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                matchingRawIdPairs);
829b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        findIdPairs(db, buildIdentityMatchingSql(rawContactIds, rawContactIds,
830b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                /* isIdentityMatching =*/ true, /* countOnly =*/false), matchingRawIdPairs);
831b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        findIdPairs(db, buildEmailMatchingSql(rawContactIds, rawContactIds, /* countOnly =*/false),
832b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                matchingRawIdPairs);
833b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        findIdPairs(db, buildPhoneMatchingSql(rawContactIds, rawContactIds,  /* countOnly =*/false),
834b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                matchingRawIdPairs);
835b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
836b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return ContactAggregatorHelper.findConnectedComponents(rawContactIdSet, matchingRawIdPairs);
837b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
838b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
839b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
840b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Given a query which will return two non-null IDs in the first two columns as results, this
841b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * method will put two entries into the given result map for each pair of different IDs, one
842b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * keyed by each ID.
843b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
8443a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void findIdPairs(SQLiteDatabase db, String query,
8453a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki            Multimap<Long, Long> results) {
846b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor cursor = db.rawQuery(query, null);
847b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
848b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.moveToPosition(-1);
849b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (cursor.moveToNext()) {
850b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long idA = cursor.getLong(0);
851b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long idB = cursor.getLong(1);
852b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (idA != idB) {
853b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    results.put(idA, idB);
854b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    results.put(idB, idA);
855b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
856b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
857b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
858b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.close();
859b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
860b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
861b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
862b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
863b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Creates a new Contact for a given set of the raw contacts of {@code rawContactIds} if the
864b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * given contactId is null. Otherwise, regroup them into contact with {@code contactId}.
865b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
8663a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void createContactForRawContacts(SQLiteDatabase db,
8673a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki            TransactionContext txContext, Set<Long> rawContactIds, Long contactId) {
868b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (rawContactIds.isEmpty()) {
869b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // No raw contact id is provided.
870b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
871b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
872b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
873b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // If contactId is not provided, generates a new one.
874b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == null) {
875aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu            mSelectionArgs1[0] = String.valueOf(rawContactIds.iterator().next());
876b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            computeAggregateData(db, mRawContactsQueryByRawContactId, mSelectionArgs1,
877b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    mContactInsert);
878b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            contactId = mContactInsert.executeInsert();
879b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
880b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (Long rawContactId : rawContactIds) {
881b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            setContactIdAndMarkAggregated(rawContactId, contactId);
882b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            setPresenceContactId(rawContactId, contactId);
883b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
884b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        updateAggregateData(txContext, contactId);
885b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
886b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
887aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected static class RawContactIdQuery {
888b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String TABLE = Tables.RAW_CONTACTS;
889aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        public static final String[] COLUMNS = {RawContacts._ID, RawContactsColumns.ACCOUNT_ID };
890b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final String SELECTION = RawContacts.CONTACT_ID + "=?";
891b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public static final int RAW_CONTACT_ID = 0;
892aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        public static final int ACCOUNT_ID = 1;
893b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
894b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
895b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
896b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Updates the contact ID for the specified contact.
897b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
8983a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void setContactId(long rawContactId, long contactId) {
899b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdUpdate.bindLong(1, contactId);
900b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdUpdate.bindLong(2, rawContactId);
901b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdUpdate.execute();
902b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
903b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
904b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
905d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu     * Marks the list of raw contact IDs as aggregated.
906d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu     *
907d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu     * @param rawContactIds comma separated raw contact ids
908b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
909d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu    protected final void markAggregated(SQLiteDatabase db, String rawContactIds) {
910d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu        final String sql = "UPDATE " + Tables.RAW_CONTACTS +
911d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu                " SET " + RawContactsColumns.AGGREGATION_NEEDED + "=0" +
912d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu                " WHERE " + RawContacts._ID + " in (" + rawContactIds + ")";
913d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu        db.execSQL(sql);
914b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
915b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
916b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
917b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Updates the contact ID for the specified contact and marks the raw contact as aggregated.
918b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
919b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void setContactIdAndMarkAggregated(long rawContactId, long contactId) {
920b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdAndMarkAggregatedUpdate.bindLong(1, contactId);
921b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdAndMarkAggregatedUpdate.bindLong(2, rawContactId);
922b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mContactIdAndMarkAggregatedUpdate.execute();
923b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
924b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
925b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void setPresenceContactId(long rawContactId, long contactId) {
926b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPresenceContactIdUpdate.bindLong(1, contactId);
927b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPresenceContactIdUpdate.bindLong(2, rawContactId);
928b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPresenceContactIdUpdate.execute();
929b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
930b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
931b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void unpinRawContact(long rawContactId) {
932b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mResetPinnedForRawContact.bindLong(1, rawContactId);
933b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mResetPinnedForRawContact.execute();
934b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
935b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
936b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    interface AggregateExceptionPrefetchQuery {
937b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.AGGREGATION_EXCEPTIONS;
938b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
939b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = {
940aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.RAW_CONTACT_ID1,
941aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.RAW_CONTACT_ID2,
942b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
943b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
944b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int RAW_CONTACT_ID1 = 0;
945b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int RAW_CONTACT_ID2 = 1;
946b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
947b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
948b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    // A set of raw contact IDs for which there are aggregation exceptions
949aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final HashSet<Long> mAggregationExceptionIds = new HashSet<Long>();
950aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected boolean mAggregationExceptionIdsValid;
951b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
9523a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void invalidateAggregationExceptionCache() {
953b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregationExceptionIdsValid = false;
954b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
955b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
956b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
957b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Finds all raw contact IDs for which there are aggregation exceptions. The list of
958b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * ids is used as an optimization in aggregation: there is no point to run a query against
959b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * the agg_exceptions table if it is known that there are no records there for a given
960b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * raw contact ID.
961b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
9623a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void prefetchAggregationExceptionIds(SQLiteDatabase db) {
963b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregationExceptionIds.clear();
964b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.query(AggregateExceptionPrefetchQuery.TABLE,
965b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                AggregateExceptionPrefetchQuery.COLUMNS,
966b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                null, null, null, null, null);
967b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
968b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
969b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
970b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId1 = c.getLong(AggregateExceptionPrefetchQuery.RAW_CONTACT_ID1);
971b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId2 = c.getLong(AggregateExceptionPrefetchQuery.RAW_CONTACT_ID2);
972b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mAggregationExceptionIds.add(rawContactId1);
973b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mAggregationExceptionIds.add(rawContactId2);
974b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
975b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
976b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
977b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
978b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
979b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mAggregationExceptionIdsValid = true;
980b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
981b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
982aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface NameLookupQuery {
983b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.NAME_LOOKUP;
984b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
985b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SELECTION = NameLookupColumns.RAW_CONTACT_ID + "=?";
986b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SELECTION_STRUCTURED_NAME_BASED =
987b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                SELECTION + " AND " + STRUCTURED_NAME_BASED_LOOKUP_SQL;
988b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
989b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
990b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupColumns.NORMALIZED_NAME,
991b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupColumns.NAME_TYPE
992b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
993b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
994b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int NORMALIZED_NAME = 0;
995b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int NAME_TYPE = 1;
996b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
997b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
9983a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void loadNameMatchCandidates(SQLiteDatabase db, long rawContactId,
999b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            MatchCandidateList candidates, boolean structuredNameBased) {
1000b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        candidates.clear();
1001b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(rawContactId);
1002b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor c = db.query(NameLookupQuery.TABLE, NameLookupQuery.COLUMNS,
1003b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                structuredNameBased
1004b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        ? NameLookupQuery.SELECTION_STRUCTURED_NAME_BASED
1005b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        : NameLookupQuery.SELECTION,
1006b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mSelectionArgs1, null, null, null);
1007b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1008b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1009b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String normalizedName = c.getString(NameLookupQuery.NORMALIZED_NAME);
1010b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int type = c.getInt(NameLookupQuery.NAME_TYPE);
1011b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                candidates.add(normalizedName, type);
1012b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1013b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1014b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1015b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1016b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1017b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1018aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    interface AggregateExceptionQuery {
1019aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        String TABLE = Tables.AGGREGATION_EXCEPTIONS
1020aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                + " JOIN raw_contacts raw_contacts1 "
1021aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                + " ON (agg_exceptions.raw_contact_id1 = raw_contacts1._id) "
1022aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                + " JOIN raw_contacts raw_contacts2 "
1023aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                + " ON (agg_exceptions.raw_contact_id2 = raw_contacts2._id) ";
1024b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1025aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        String[] COLUMNS = {
1026aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.TYPE,
1027aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.RAW_CONTACT_ID1,
1028aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts1." + RawContacts.CONTACT_ID,
1029aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts1." + RawContactsColumns.ACCOUNT_ID,
1030aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts1." + RawContactsColumns.AGGREGATION_NEEDED,
1031aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                AggregationExceptions.RAW_CONTACT_ID2,
1032aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts2." + RawContacts.CONTACT_ID,
1033aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts2." + RawContactsColumns.ACCOUNT_ID,
1034aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                "raw_contacts2." + RawContactsColumns.AGGREGATION_NEEDED,
1035aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        };
1036aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu
1037aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int TYPE = 0;
1038aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int RAW_CONTACT_ID1 = 1;
1039aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int CONTACT_ID1 = 2;
1040aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int ACCOUNT_ID1 = 3;
1041aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int AGGREGATION_NEEDED_1 = 4;
1042aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int RAW_CONTACT_ID2 = 5;
1043aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int CONTACT_ID2 = 6;
1044aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int ACCOUNT_ID2 = 7;
1045aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int AGGREGATION_NEEDED_2 = 8;
1046b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1047b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1048aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface NameLookupMatchQueryWithParameter {
1049b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.NAME_LOOKUP
1050b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.RAW_CONTACTS +
1051b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " ON (" + NameLookupColumns.RAW_CONTACT_ID + " = "
1052b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Tables.RAW_CONTACTS + "." + RawContacts._ID + ")";
1053b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1054b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
1055aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContacts._ID,
1056aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContacts.CONTACT_ID,
1057aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContactsColumns.ACCOUNT_ID,
1058aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                NameLookupColumns.NORMALIZED_NAME,
1059aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                NameLookupColumns.NAME_TYPE,
1060b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1061b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1062aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int RAW_CONTACT_ID = 0;
1063aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int CONTACT_ID = 1;
1064aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int ACCOUNT_ID = 2;
1065aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int NAME = 3;
1066aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int NAME_TYPE = 4;
1067b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1068b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1069aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected final class NameLookupSelectionBuilder extends NameLookupBuilder {
1070b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1071b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        private final MatchCandidateList mNameLookupCandidates;
1072b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1073b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        private StringBuilder mSelection = new StringBuilder(
1074b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupColumns.NORMALIZED_NAME + " IN(");
1075b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1076b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1077b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public NameLookupSelectionBuilder(NameSplitter splitter, MatchCandidateList candidates) {
1078b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            super(splitter);
1079b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            this.mNameLookupCandidates = candidates;
1080b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1081b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1082b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        @Override
1083b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        protected String[] getCommonNicknameClusters(String normalizedName) {
1084b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return mCommonNicknameCache.getCommonNicknameClusters(normalizedName);
1085b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1086b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1087b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        @Override
1088b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        protected void insertNameLookup(
1089b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId, long dataId, int lookupType, String string) {
1090b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mNameLookupCandidates.add(string, lookupType);
1091b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            DatabaseUtils.appendEscapedSQLString(mSelection, string);
1092b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mSelection.append(',');
1093b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1094b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1095b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public boolean isEmpty() {
1096b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return mNameLookupCandidates.isEmpty();
1097b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1098b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1099b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public String getSelection() {
1100b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mSelection.setLength(mSelection.length() - 1);      // Strip last comma
1101b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mSelection.append(')');
1102b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return mSelection.toString();
1103b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1104b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1105b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public int getLookupType(String name) {
1106b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            for (int i = 0; i < mNameLookupCandidates.mCount; i++) {
1107b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (mNameLookupCandidates.mList.get(i).mName.equals(name)) {
1108b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    return mNameLookupCandidates.mList.get(i).mLookupType;
1109b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1110b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1111b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            throw new IllegalStateException();
1112b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1113b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1114b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1115b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1116b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Finds contacts with names matching the specified name.
1117b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
11183a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void updateMatchScoresBasedOnNameMatches(SQLiteDatabase db, String query,
1119b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            MatchCandidateList candidates, ContactMatcher matcher) {
1120b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        candidates.clear();
1121b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        NameLookupSelectionBuilder builder = new NameLookupSelectionBuilder(
1122b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mNameSplitter, candidates);
1123b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        builder.insertNameLookup(0, 0, query, FullNameStyle.UNDEFINED);
1124b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (builder.isEmpty()) {
1125b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1126b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1127b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1128b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor c = db.query(NameLookupMatchQueryWithParameter.TABLE,
1129b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupMatchQueryWithParameter.COLUMNS, builder.getSelection(), null, null, null,
1130b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                null, PRIMARY_HIT_LIMIT_STRING);
1131b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1132b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1133b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long contactId = c.getLong(NameLookupMatchQueryWithParameter.CONTACT_ID);
1134b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String name = c.getString(NameLookupMatchQueryWithParameter.NAME);
1135b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int nameTypeA = builder.getLookupType(name);
1136b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int nameTypeB = c.getInt(NameLookupMatchQueryWithParameter.NAME_TYPE);
1137b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                matcher.matchName(contactId, nameTypeA, name, nameTypeB, name,
1138b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        ContactMatcher.MATCHING_ALGORITHM_EXACT);
1139b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (nameTypeA == NameLookupType.NICKNAME && nameTypeB == NameLookupType.NICKNAME) {
1140b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    matcher.updateScoreWithNicknameMatch(contactId);
1141b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1142b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1143b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1144b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1145b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1146b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1147b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1148aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface EmailLookupQuery {
1149b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.DATA + " dataA"
1150b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.DATA + " dataB" +
1151d0d961e19f19704f7967dadf5be61fe6273a1d32Zheng Fu                " ON dataA." + Email.DATA + "= dataB." + Email.DATA
1152b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.RAW_CONTACTS +
1153b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " ON (dataB." + Data.RAW_CONTACT_ID + " = "
1154b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Tables.RAW_CONTACTS + "." + RawContacts._ID + ")";
1155b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1156b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SELECTION = "dataA." + Data.RAW_CONTACT_ID + "=?1"
1157b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND dataA." + DataColumns.MIMETYPE_ID + "=?2"
1158b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND dataA." + Email.DATA + " NOT NULL"
1159b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND dataB." + DataColumns.MIMETYPE_ID + "=?2"
1160b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0"
1161b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
1162b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1163b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
1164aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                Tables.RAW_CONTACTS + "." + RawContacts._ID,
1165aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContacts.CONTACT_ID,
1166aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContactsColumns.ACCOUNT_ID
1167b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1168b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1169aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int RAW_CONTACT_ID = 0;
1170aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int CONTACT_ID = 1;
1171aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int ACCOUNT_ID = 2;
1172b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1173b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1174aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface PhoneLookupQuery {
1175b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.PHONE_LOOKUP + " phoneA"
1176b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.DATA + " dataA"
1177b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " ON (dataA." + Data._ID + "=phoneA." + PhoneLookupColumns.DATA_ID + ")"
1178b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.PHONE_LOOKUP + " phoneB"
1179b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " ON (phoneA." + PhoneLookupColumns.MIN_MATCH + "="
1180b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + "phoneB." + PhoneLookupColumns.MIN_MATCH + ")"
1181b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.DATA + " dataB"
1182b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " ON (dataB." + Data._ID + "=phoneB." + PhoneLookupColumns.DATA_ID + ")"
1183b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.RAW_CONTACTS
1184b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " ON (dataB." + Data.RAW_CONTACT_ID + " = "
1185b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Tables.RAW_CONTACTS + "." + RawContacts._ID + ")";
1186b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1187b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SELECTION = "dataA." + Data.RAW_CONTACT_ID + "=?"
1188b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND PHONE_NUMBERS_EQUAL(dataA." + Phone.NUMBER + ", "
1189b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + "dataB." + Phone.NUMBER + ",?)"
1190b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + RawContactsColumns.AGGREGATION_NEEDED + "=0"
1191b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + RawContacts.CONTACT_ID + " IN " + Tables.DEFAULT_DIRECTORY;
1192b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1193b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
1194aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                Tables.RAW_CONTACTS + "." + RawContacts._ID,
1195aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContacts.CONTACT_ID,
1196aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu                RawContactsColumns.ACCOUNT_ID
1197b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1198b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1199aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int RAW_CONTACT_ID = 0;
1200aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int CONTACT_ID = 1;
1201aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        int ACCOUNT_ID = 2;
1202b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1203b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1204b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface ContactNameLookupQuery {
1205b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Tables.NAME_LOOKUP_JOIN_RAW_CONTACTS;
1206b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1207aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu        String[] COLUMNS = new String[]{
1208b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContacts.CONTACT_ID,
1209b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupColumns.NORMALIZED_NAME,
1210b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                NameLookupColumns.NAME_TYPE
1211b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1212b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1213b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CONTACT_ID = 0;
1214b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int NORMALIZED_NAME = 1;
1215b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int NAME_TYPE = 2;
1216b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1217b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1218b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1219b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Loads all candidate rows from the name lookup table and updates match scores based
1220b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * on that data.
1221b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
1222b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void matchAllCandidates(SQLiteDatabase db, String selection,
1223b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            MatchCandidateList candidates, ContactMatcher matcher, int algorithm, String limit) {
1224b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.query(ContactNameLookupQuery.TABLE, ContactNameLookupQuery.COLUMNS,
1225b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                selection, null, null, null, null, limit);
1226b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1227b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1228b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1229b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                Long contactId = c.getLong(ContactNameLookupQuery.CONTACT_ID);
1230b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String name = c.getString(ContactNameLookupQuery.NORMALIZED_NAME);
1231b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int nameType = c.getInt(ContactNameLookupQuery.NAME_TYPE);
1232b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1233b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // Note the N^2 complexity of the following fragment. This is not a huge concern
1234b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // since the number of candidates is very small and in general secondary hits
1235b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // in the absence of primary hits are rare.
1236b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                for (int i = 0; i < candidates.mCount; i++) {
1237b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    NameMatchCandidate candidate = candidates.mList.get(i);
1238b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    matcher.matchName(contactId, candidate.mLookupType, candidate.mName,
1239b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            nameType, name, algorithm);
1240b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1241b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1242b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1243b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1244b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1245b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1246b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1247b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface RawContactsQuery {
1248b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL_FORMAT_HAS_SUPER_PRIMARY_NAME =
1249b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " EXISTS(SELECT 1 " +
1250b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " FROM " + Tables.DATA + " d " +
1251b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " WHERE d." + DataColumns.MIMETYPE_ID + "=%d " +
1252b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " AND d." + Data.RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID +
1253b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " AND d." + Data.IS_SUPER_PRIMARY + "=1)";
1254b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1255b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL_FORMAT =
1256b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "SELECT "
1257b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.CONCRETE_ID + ","
1258b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.DISPLAY_NAME + ","
1259b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.DISPLAY_NAME_SOURCE + ","
1260b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + AccountsColumns.CONCRETE_ACCOUNT_TYPE + ","
1261b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + AccountsColumns.CONCRETE_ACCOUNT_NAME + ","
1262b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + AccountsColumns.CONCRETE_DATA_SET + ","
1263b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.SOURCE_ID + ","
1264b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.CUSTOM_RINGTONE + ","
1265b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.SEND_TO_VOICEMAIL + ","
1266e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + RawContacts.RAW_LAST_TIME_CONTACTED + ","
1267e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + RawContacts.RAW_TIMES_CONTACTED + ","
1268b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.STARRED + ","
1269b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.PINNED + ","
1270b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + DataColumns.CONCRETE_ID + ","
1271b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + DataColumns.CONCRETE_MIMETYPE_ID + ","
1272b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Data.IS_SUPER_PRIMARY + ","
1273b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Photo.PHOTO_FILE_ID + ","
1274b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + SQL_FORMAT_HAS_SUPER_PRIMARY_NAME +
1275b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Tables.RAW_CONTACTS +
1276b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " JOIN " + Tables.ACCOUNTS + " ON ("
1277b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
1278b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + ")" +
1279b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " LEFT OUTER JOIN " + Tables.DATA +
1280b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " ON (" + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
1281b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " AND ((" + DataColumns.MIMETYPE_ID + "=%d"
1282b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                + " AND " + Photo.PHOTO + " NOT NULL)"
1283b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " OR (" + DataColumns.MIMETYPE_ID + "=%d"
1284b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                + " AND " + Phone.NUMBER + " NOT NULL)))";
1285b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1286b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL_FORMAT_BY_RAW_CONTACT_ID = SQL_FORMAT +
1287b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContactsColumns.CONCRETE_ID + "=?";
1288b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1289b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL_FORMAT_BY_CONTACT_ID = SQL_FORMAT +
1290b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts.CONTACT_ID + "=?"
1291b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND " + RawContacts.DELETED + "=0";
1292b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1293b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int RAW_CONTACT_ID = 0;
1294b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DISPLAY_NAME = 1;
1295b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DISPLAY_NAME_SOURCE = 2;
1296b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_TYPE = 3;
1297b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_NAME = 4;
1298b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DATA_SET = 5;
1299b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int SOURCE_ID = 6;
1300b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CUSTOM_RINGTONE = 7;
1301b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int SEND_TO_VOICEMAIL = 8;
1302e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        int RAW_LAST_TIME_CONTACTED = 9;
1303e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        int RAW_TIMES_CONTACTED = 10;
1304b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int STARRED = 11;
1305b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PINNED = 12;
1306b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DATA_ID = 13;
1307b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int MIMETYPE_ID = 14;
1308b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int IS_SUPER_PRIMARY = 15;
1309b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PHOTO_FILE_ID = 16;
1310b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int HAS_SUPER_PRIMARY_NAME = 17;
1311b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1312b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1313aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected interface ContactReplaceSqlStatement {
1314b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String UPDATE_SQL =
1315b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.CONTACTS +
1316b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET "
1317b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.NAME_RAW_CONTACT_ID + "=?, "
1318b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PHOTO_ID + "=?, "
1319b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PHOTO_FILE_ID + "=?, "
1320b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.SEND_TO_VOICEMAIL + "=?, "
1321b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.CUSTOM_RINGTONE + "=?, "
1322e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + Contacts.RAW_LAST_TIME_CONTACTED + "=?, "
1323e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + Contacts.RAW_TIMES_CONTACTED + "=?, "
1324b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.STARRED + "=?, "
1325b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PINNED + "=?, "
1326b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.HAS_PHONE_NUMBER + "=?, "
1327b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.LOOKUP_KEY + "=?, "
1328b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + "=? " +
1329b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + Contacts._ID + "=?";
1330b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1331b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String INSERT_SQL =
1332b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "INSERT INTO " + Tables.CONTACTS + " ("
1333b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.NAME_RAW_CONTACT_ID + ", "
1334b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PHOTO_ID + ", "
1335b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PHOTO_FILE_ID + ", "
1336b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.SEND_TO_VOICEMAIL + ", "
1337b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.CUSTOM_RINGTONE + ", "
1338e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + Contacts.RAW_LAST_TIME_CONTACTED + ", "
1339e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                        + Contacts.RAW_TIMES_CONTACTED + ", "
1340b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.STARRED + ", "
1341b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.PINNED + ", "
1342b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.HAS_PHONE_NUMBER + ", "
1343b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.LOOKUP_KEY + ", "
1344b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Contacts.CONTACT_LAST_UPDATED_TIMESTAMP
1345b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + ") " +
1346b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
1347b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1348b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int NAME_RAW_CONTACT_ID = 1;
1349b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PHOTO_ID = 2;
1350b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PHOTO_FILE_ID = 3;
1351b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int SEND_TO_VOICEMAIL = 4;
1352b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CUSTOM_RINGTONE = 5;
1353e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        int RAW_LAST_TIME_CONTACTED = 6;
1354e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        int RAW_TIMES_CONTACTED = 7;
1355b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int STARRED = 8;
1356b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PINNED = 9;
1357b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int HAS_PHONE_NUMBER = 10;
1358b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int LOOKUP_KEY = 11;
1359b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CONTACT_LAST_UPDATED_TIMESTAMP = 12;
1360b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int CONTACT_ID = 13;
1361b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1362b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1363b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1364b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Computes aggregate-level data for the specified aggregate contact ID.
1365b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
1366aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected void computeAggregateData(SQLiteDatabase db, long contactId,
1367b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            SQLiteStatement statement) {
1368b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(contactId);
1369b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        computeAggregateData(db, mRawContactsQueryByContactId, mSelectionArgs1, statement);
1370b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1371b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1372b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1373b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Indicates whether the given photo entry and priority gives this photo a higher overall
1374b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * priority than the current best photo entry and priority.
1375b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
1376b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private boolean hasHigherPhotoPriority(PhotoEntry photoEntry, int priority,
1377b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            PhotoEntry bestPhotoEntry, int bestPriority) {
1378b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int photoComparison = photoEntry.compareTo(bestPhotoEntry);
1379b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return photoComparison < 0 || photoComparison == 0 && priority > bestPriority;
1380b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1381b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1382b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1383b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Computes aggregate-level data from constituent raw contacts.
1384b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
13853a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    protected final void computeAggregateData(final SQLiteDatabase db, String sql, String[] sqlArgs,
1386b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            SQLiteStatement statement) {
1387b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long currentRawContactId = -1;
1388b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long bestPhotoId = -1;
1389b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long bestPhotoFileId = 0;
1390b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        PhotoEntry bestPhotoEntry = null;
1391b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        boolean foundSuperPrimaryPhoto = false;
1392b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int photoPriority = -1;
1393b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int totalRowCount = 0;
1394b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int contactSendToVoicemail = 0;
1395b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String contactCustomRingtone = null;
1396b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactLastTimeContacted = 0;
1397b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int contactTimesContacted = 0;
1398b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int contactStarred = 0;
1399b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int contactPinned = Integer.MAX_VALUE;
1400b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int hasPhoneNumber = 0;
1401b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        StringBuilder lookupKey = new StringBuilder();
1402b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1403b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDisplayNameCandidate.clear();
1404b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1405b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor c = db.rawQuery(sql, sqlArgs);
1406b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1407b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1408b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId = c.getLong(RawContactsQuery.RAW_CONTACT_ID);
1409b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (rawContactId != currentRawContactId) {
1410b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    currentRawContactId = rawContactId;
1411b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    totalRowCount++;
1412b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1413b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // Assemble sub-account.
1414b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
1415b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    String dataSet = c.getString(RawContactsQuery.DATA_SET);
1416b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    String accountWithDataSet = (!TextUtils.isEmpty(dataSet))
1417b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            ? accountType + "/" + dataSet
1418b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            : accountType;
1419b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1420b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // Display name
1421b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    String displayName = c.getString(RawContactsQuery.DISPLAY_NAME);
1422b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    int displayNameSource = c.getInt(RawContactsQuery.DISPLAY_NAME_SOURCE);
1423b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    int isNameSuperPrimary = c.getInt(RawContactsQuery.HAS_SUPER_PRIMARY_NAME);
1424b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    processDisplayNameCandidate(rawContactId, displayName, displayNameSource,
1425b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            mContactsProvider.isWritableAccountWithDataSet(accountWithDataSet),
1426b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            isNameSuperPrimary != 0);
1427b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1428b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // Contact options
1429b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (!c.isNull(RawContactsQuery.SEND_TO_VOICEMAIL)) {
1430b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        boolean sendToVoicemail =
1431b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                (c.getInt(RawContactsQuery.SEND_TO_VOICEMAIL) != 0);
1432b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        if (sendToVoicemail) {
1433b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            contactSendToVoicemail++;
1434b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        }
1435b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1436b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1437b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (contactCustomRingtone == null
1438b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            && !c.isNull(RawContactsQuery.CUSTOM_RINGTONE)) {
1439b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        contactCustomRingtone = c.getString(RawContactsQuery.CUSTOM_RINGTONE);
1440b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1441b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1442e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                    long lastTimeContacted = c.getLong(RawContactsQuery.RAW_LAST_TIME_CONTACTED);
1443b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (lastTimeContacted > contactLastTimeContacted) {
1444b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        contactLastTimeContacted = lastTimeContacted;
1445b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1446b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1447e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki                    int timesContacted = c.getInt(RawContactsQuery.RAW_TIMES_CONTACTED);
1448b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (timesContacted > contactTimesContacted) {
1449b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        contactTimesContacted = timesContacted;
1450b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1451b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1452b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (c.getInt(RawContactsQuery.STARRED) != 0) {
1453b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        contactStarred = 1;
1454b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1455b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1456b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // contactPinned should be the lowest value of its constituent raw contacts,
1457b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // excluding negative integers
1458b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    final int rawContactPinned = c.getInt(RawContactsQuery.PINNED);
1459b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (rawContactPinned > PinnedPositions.UNPINNED) {
1460b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        contactPinned = Math.min(contactPinned, rawContactPinned);
1461b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1462b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1463b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    appendLookupKey(
1464b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            lookupKey,
1465b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            accountWithDataSet,
1466b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            c.getString(RawContactsQuery.ACCOUNT_NAME),
1467b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            rawContactId,
1468b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            c.getString(RawContactsQuery.SOURCE_ID),
1469b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            displayName);
1470b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1471b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1472b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (!c.isNull(RawContactsQuery.DATA_ID)) {
1473b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    long dataId = c.getLong(RawContactsQuery.DATA_ID);
1474b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    long photoFileId = c.getLong(RawContactsQuery.PHOTO_FILE_ID);
1475b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    int mimetypeId = c.getInt(RawContactsQuery.MIMETYPE_ID);
1476b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    boolean superPrimary = c.getInt(RawContactsQuery.IS_SUPER_PRIMARY) != 0;
1477b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (mimetypeId == mMimeTypeIdPhoto) {
1478b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        if (!foundSuperPrimaryPhoto) {
1479b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            // Lookup the metadata for the photo, if available.  Note that data set
1480b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            // does not come into play here, since accounts are looked up in the
1481b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            // account manager in the priority resolver.
1482b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            PhotoEntry photoEntry = getPhotoMetadata(db, photoFileId);
1483b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
1484b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            int priority = mPhotoPriorityResolver.getPhotoPriority(accountType);
1485b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            if (superPrimary || hasHigherPhotoPriority(
1486b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                    photoEntry, priority, bestPhotoEntry, photoPriority)) {
1487b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                bestPhotoEntry = photoEntry;
1488b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                photoPriority = priority;
1489b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                bestPhotoId = dataId;
1490b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                bestPhotoFileId = photoFileId;
1491b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                foundSuperPrimaryPhoto |= superPrimary;
1492b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            }
1493b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        }
1494b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    } else if (mimetypeId == mMimeTypeIdPhone) {
1495b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        hasPhoneNumber = 1;
1496b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1497b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1498b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1499b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1500b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1501b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1502b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1503b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactPinned == Integer.MAX_VALUE) {
1504b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            contactPinned = PinnedPositions.UNPINNED;
1505b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1506b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1507b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.NAME_RAW_CONTACT_ID,
1508b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                mDisplayNameCandidate.rawContactId);
1509b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1510b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (bestPhotoId != -1) {
1511b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            statement.bindLong(ContactReplaceSqlStatement.PHOTO_ID, bestPhotoId);
1512b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1513b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            statement.bindNull(ContactReplaceSqlStatement.PHOTO_ID);
1514b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1515b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1516b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (bestPhotoFileId != 0) {
1517b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            statement.bindLong(ContactReplaceSqlStatement.PHOTO_FILE_ID, bestPhotoFileId);
1518b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1519b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            statement.bindNull(ContactReplaceSqlStatement.PHOTO_FILE_ID);
1520b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1521b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1522b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.SEND_TO_VOICEMAIL,
1523b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                totalRowCount == contactSendToVoicemail ? 1 : 0);
1524b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        DatabaseUtils.bindObjectToProgram(statement, ContactReplaceSqlStatement.CUSTOM_RINGTONE,
1525b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactCustomRingtone);
1526e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        statement.bindLong(ContactReplaceSqlStatement.RAW_LAST_TIME_CONTACTED,
1527b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactLastTimeContacted);
1528e2e9ac275e487ce558579ee65ff8f122cf498b07Makoto Onuki        statement.bindLong(ContactReplaceSqlStatement.RAW_TIMES_CONTACTED,
1529b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactTimesContacted);
1530b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.STARRED,
1531b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactStarred);
1532b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.PINNED,
1533b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                contactPinned);
1534b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.HAS_PHONE_NUMBER,
1535b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                hasPhoneNumber);
1536b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindString(ContactReplaceSqlStatement.LOOKUP_KEY,
1537b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                Uri.encode(lookupKey.toString()));
1538b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        statement.bindLong(ContactReplaceSqlStatement.CONTACT_LAST_UPDATED_TIMESTAMP,
1539b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                Clock.getInstance().currentTimeMillis());
1540b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1541b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1542b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1543b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Builds a lookup key using the given data.
1544b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
15453a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    // Overridden by ProfileAggregator.
1546b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    protected void appendLookupKey(StringBuilder sb, String accountTypeWithDataSet,
1547b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            String accountName, long rawContactId, String sourceId, String displayName) {
1548b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        ContactLookupKey.appendToLookupKey(sb, accountTypeWithDataSet, accountName, rawContactId,
1549b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                sourceId, displayName);
1550b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1551b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1552b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1553b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Uses the supplied values to determine if they represent a "better" display name
1554b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * for the aggregate contact currently evaluated.  If so, it updates
1555b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * {@link #mDisplayNameCandidate} with the new values.
1556b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
1557b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void processDisplayNameCandidate(long rawContactId, String displayName,
1558b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            int displayNameSource, boolean writableAccount, boolean isNameSuperPrimary) {
1559b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1560b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        boolean replace = false;
1561b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (mDisplayNameCandidate.rawContactId == -1) {
1562b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // No previous values available
1563b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            replace = true;
1564b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else if (!TextUtils.isEmpty(displayName)) {
1565b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (isNameSuperPrimary) {
1566b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // A super primary name is better than any other name
1567b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                replace = true;
1568b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            } else if (mDisplayNameCandidate.isNameSuperPrimary == isNameSuperPrimary) {
1569b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (mDisplayNameCandidate.displayNameSource < displayNameSource) {
1570b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    // New values come from an superior source, e.g. structured name vs phone number
1571b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    replace = true;
1572b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                } else if (mDisplayNameCandidate.displayNameSource == displayNameSource) {
1573b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (!mDisplayNameCandidate.writableAccount && writableAccount) {
1574b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        replace = true;
1575b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    } else if (mDisplayNameCandidate.writableAccount == writableAccount) {
1576b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        if (NameNormalizer.compareComplexity(displayName,
1577b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                mDisplayNameCandidate.displayName) > 0) {
1578b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            // New name is more complex than the previously found one
1579b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            replace = true;
1580b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        }
1581b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1582b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1583b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1584b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1585b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1586b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (replace) {
1587b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameCandidate.rawContactId = rawContactId;
1588b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameCandidate.displayName = displayName;
1589b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameCandidate.displayNameSource = displayNameSource;
1590b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameCandidate.isNameSuperPrimary = isNameSuperPrimary;
1591b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameCandidate.writableAccount = writableAccount;
1592b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1593b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1594b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1595b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface PhotoIdQuery {
1596b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String[] COLUMNS = new String[] {
1597b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            AccountsColumns.CONCRETE_ACCOUNT_TYPE,
1598b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            DataColumns.CONCRETE_ID,
1599b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Data.IS_SUPER_PRIMARY,
1600b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Photo.PHOTO_FILE_ID,
1601b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1602b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1603b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_TYPE = 0;
1604b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DATA_ID = 1;
1605b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int IS_SUPER_PRIMARY = 2;
1606b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int PHOTO_FILE_ID = 3;
1607b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1608b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
16093a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updatePhotoId(SQLiteDatabase db, long rawContactId) {
1610b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1611b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1612b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1613b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1614b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1615b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1616b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long bestPhotoId = -1;
1617b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long bestPhotoFileId = 0;
1618b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int photoPriority = -1;
1619b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1620b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long photoMimeType = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE);
1621b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1622b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String tables = Tables.RAW_CONTACTS
1623b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.ACCOUNTS + " ON ("
1624b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + AccountsColumns.CONCRETE_ID + "=" + RawContactsColumns.CONCRETE_ACCOUNT_ID
1625b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    + ")"
1626b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " JOIN " + Tables.DATA + " ON("
1627b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID
1628b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                + " AND (" + DataColumns.MIMETYPE_ID + "=" + photoMimeType + " AND "
1629b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + Photo.PHOTO + " NOT NULL))";
1630b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1631b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(contactId);
1632b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.query(tables, PhotoIdQuery.COLUMNS,
1633b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContacts.CONTACT_ID + "=?", mSelectionArgs1, null, null, null);
1634b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1635b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            PhotoEntry bestPhotoEntry = null;
1636b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1637b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long dataId = c.getLong(PhotoIdQuery.DATA_ID);
1638b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long photoFileId = c.getLong(PhotoIdQuery.PHOTO_FILE_ID);
1639b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                boolean superPrimary = c.getInt(PhotoIdQuery.IS_SUPER_PRIMARY) != 0;
1640b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                PhotoEntry photoEntry = getPhotoMetadata(db, photoFileId);
1641b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1642b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // Note that data set does not come into play here, since accounts are looked up in
1643b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // the account manager in the priority resolver.
1644b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String accountType = c.getString(PhotoIdQuery.ACCOUNT_TYPE);
1645b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int priority = mPhotoPriorityResolver.getPhotoPriority(accountType);
1646b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (superPrimary || hasHigherPhotoPriority(
1647b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        photoEntry, priority, bestPhotoEntry, photoPriority)) {
1648b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    bestPhotoEntry = photoEntry;
1649b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    photoPriority = priority;
1650b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    bestPhotoId = dataId;
1651b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    bestPhotoFileId = photoFileId;
1652b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    if (superPrimary) {
1653b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        break;
1654b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    }
1655b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1656b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1657b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1658b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1659b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1660b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1661b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (bestPhotoId == -1) {
1662b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mPhotoIdUpdate.bindNull(1);
1663b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1664b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mPhotoIdUpdate.bindLong(1, bestPhotoId);
1665b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1666b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1667b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (bestPhotoFileId == 0) {
1668b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mPhotoIdUpdate.bindNull(2);
1669b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1670b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mPhotoIdUpdate.bindLong(2, bestPhotoFileId);
1671b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1672b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1673b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPhotoIdUpdate.bindLong(3, contactId);
1674b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPhotoIdUpdate.execute();
1675b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1676b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1677b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface PhotoFileQuery {
1678b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final String[] COLUMNS = new String[] {
1679b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                PhotoFiles.HEIGHT,
1680b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                PhotoFiles.WIDTH,
1681b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                PhotoFiles.FILESIZE
1682b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1683b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1684b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int HEIGHT = 0;
1685b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int WIDTH = 1;
1686b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int FILESIZE = 2;
1687b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1688b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1689b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private class PhotoEntry implements Comparable<PhotoEntry> {
1690b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Pixel count (width * height) for the image.
1691b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int pixelCount;
1692b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1693b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // File size (in bytes) of the image.  Not populated if the image is a thumbnail.
1694b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final int fileSize;
1695b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1696b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        private PhotoEntry(int pixelCount, int fileSize) {
1697b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            this.pixelCount = pixelCount;
1698b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            this.fileSize = fileSize;
1699b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1700b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1701b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        @Override
1702b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        public int compareTo(PhotoEntry pe) {
1703b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (pe == null) {
1704b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                return -1;
1705b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1706b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (pixelCount == pe.pixelCount) {
1707b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                return pe.fileSize - fileSize;
1708b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            } else {
1709b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                return pe.pixelCount - pixelCount;
1710b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1711b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1712b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1713b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1714b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private PhotoEntry getPhotoMetadata(SQLiteDatabase db, long photoFileId) {
1715b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (photoFileId == 0) {
1716b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // Assume standard thumbnail size.  Don't bother getting a file size for priority;
1717b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            // we should fall back to photo priority resolver if all we have are thumbnails.
1718b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            int thumbDim = mContactsProvider.getMaxThumbnailDim();
1719b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return new PhotoEntry(thumbDim * thumbDim, 0);
1720b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1721b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Cursor c = db.query(Tables.PHOTO_FILES, PhotoFileQuery.COLUMNS, PhotoFiles._ID + "=?",
1722b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    new String[]{String.valueOf(photoFileId)}, null, null, null);
1723b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            try {
1724b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                if (c.getCount() == 1) {
1725b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    c.moveToFirst();
1726b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    int pixelCount =
1727b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                            c.getInt(PhotoFileQuery.HEIGHT) * c.getInt(PhotoFileQuery.WIDTH);
1728b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                    return new PhotoEntry(pixelCount, c.getInt(PhotoFileQuery.FILESIZE));
1729b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                }
1730b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            } finally {
1731b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                c.close();
1732b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1733b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1734b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return new PhotoEntry(0, 0);
1735b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1736b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1737b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface DisplayNameQuery {
1738b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL_HAS_SUPER_PRIMARY_NAME =
1739b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " EXISTS(SELECT 1 " +
1740b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " FROM " + Tables.DATA + " d " +
1741b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " WHERE d." + DataColumns.MIMETYPE_ID + "=? " +
1742b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " AND d." + Data.RAW_CONTACT_ID + "=" + Views.RAW_CONTACTS
1743b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + "." + RawContacts._ID +
1744b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        " AND d." + Data.IS_SUPER_PRIMARY + "=1)";
1745b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1746b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String SQL =
1747b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "SELECT "
1748b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts._ID + ","
1749b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.DISPLAY_NAME + ","
1750b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContactsColumns.DISPLAY_NAME_SOURCE + ","
1751b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + SQL_HAS_SUPER_PRIMARY_NAME + ","
1752b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.SOURCE_ID + ","
1753b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + RawContacts.ACCOUNT_TYPE_AND_DATA_SET +
1754b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " FROM " + Views.RAW_CONTACTS +
1755b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + RawContacts.CONTACT_ID + "=? ";
1756b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1757b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int _ID = 0;
1758b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DISPLAY_NAME = 1;
1759b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DISPLAY_NAME_SOURCE = 2;
1760b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int HAS_SUPER_PRIMARY_NAME = 3;
1761b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int SOURCE_ID = 4;
1762b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_TYPE_AND_DATA_SET = 5;
1763b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1764b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
17653a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateDisplayNameForRawContact(SQLiteDatabase db, long rawContactId) {
1766b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1767b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1768b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1769b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1770b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1771b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        updateDisplayNameForContact(db, contactId);
1772b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1773b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
17743a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateDisplayNameForContact(SQLiteDatabase db, long contactId) {
1775b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        boolean lookupKeyUpdateNeeded = false;
1776b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1777b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mDisplayNameCandidate.clear();
1778b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1779b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs2[0] = String.valueOf(mDbHelper.getMimeTypeIdForStructuredName());
1780b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs2[1] = String.valueOf(contactId);
1781b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.rawQuery(DisplayNameQuery.SQL, mSelectionArgs2);
1782b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1783b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1784b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                long rawContactId = c.getLong(DisplayNameQuery._ID);
1785b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String displayName = c.getString(DisplayNameQuery.DISPLAY_NAME);
1786b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int displayNameSource = c.getInt(DisplayNameQuery.DISPLAY_NAME_SOURCE);
1787b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                int isNameSuperPrimary = c.getInt(DisplayNameQuery.HAS_SUPER_PRIMARY_NAME);
1788b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                String accountTypeAndDataSet = c.getString(
1789b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        DisplayNameQuery.ACCOUNT_TYPE_AND_DATA_SET);
1790b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                processDisplayNameCandidate(rawContactId, displayName, displayNameSource,
1791b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        mContactsProvider.isWritableAccountWithDataSet(accountTypeAndDataSet),
1792b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        isNameSuperPrimary != 0);
1793b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1794b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // If the raw contact has no source id, the lookup key is based on the display
1795b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                // name, so the lookup key needs to be updated.
1796b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                lookupKeyUpdateNeeded |= c.isNull(DisplayNameQuery.SOURCE_ID);
1797b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1798b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1799b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1800b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1801b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1802b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (mDisplayNameCandidate.rawContactId != -1) {
1803b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameUpdate.bindLong(1, mDisplayNameCandidate.rawContactId);
1804b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameUpdate.bindLong(2, contactId);
1805b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mDisplayNameUpdate.execute();
1806b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1807b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1808b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (lookupKeyUpdateNeeded) {
1809b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            updateLookupKeyForContact(db, contactId);
1810b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1811b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1812b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1813b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1814b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1815b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Updates the {@link Contacts#HAS_PHONE_NUMBER} flag for the aggregate contact containing the
1816b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * specified raw contact.
1817b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
18183a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateHasPhoneNumber(SQLiteDatabase db, long rawContactId) {
1819b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1820b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1821b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1822b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1823b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1824b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1825b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final SQLiteStatement hasPhoneNumberUpdate = db.compileStatement(
1826b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                "UPDATE " + Tables.CONTACTS +
1827b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " SET " + Contacts.HAS_PHONE_NUMBER + "="
1828b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + "(SELECT (CASE WHEN COUNT(*)=0 THEN 0 ELSE 1 END)"
1829b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " FROM " + Tables.DATA_JOIN_RAW_CONTACTS
1830b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        + " WHERE " + DataColumns.MIMETYPE_ID + "=?"
1831b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                + " AND " + Phone.NUMBER + " NOT NULL"
1832b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                                + " AND " + RawContacts.CONTACT_ID + "=?)" +
1833b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                " WHERE " + Contacts._ID + "=?");
1834b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1835b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            hasPhoneNumberUpdate.bindLong(1, mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE));
1836b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            hasPhoneNumberUpdate.bindLong(2, contactId);
1837b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            hasPhoneNumberUpdate.bindLong(3, contactId);
1838b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            hasPhoneNumberUpdate.execute();
1839b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1840b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            hasPhoneNumberUpdate.close();
1841b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1842b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1843b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1844b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface LookupKeyQuery {
1845b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String TABLE = Views.RAW_CONTACTS;
1846b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
1847b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            RawContacts._ID,
1848b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            RawContactsColumns.DISPLAY_NAME,
1849b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            RawContacts.ACCOUNT_TYPE_AND_DATA_SET,
1850b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            RawContacts.ACCOUNT_NAME,
1851b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            RawContacts.SOURCE_ID,
1852b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1853b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1854b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ID = 0;
1855b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int DISPLAY_NAME = 1;
1856b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_TYPE_AND_DATA_SET = 2;
1857b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int ACCOUNT_NAME = 3;
1858b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int SOURCE_ID = 4;
1859b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1860b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
18613a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateLookupKeyForRawContact(SQLiteDatabase db, long rawContactId) {
1862b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1863b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1864b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1865b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1866b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1867b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        updateLookupKeyForContact(db, contactId);
1868b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1869b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1870b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private void updateLookupKeyForContact(SQLiteDatabase db, long contactId) {
1871b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String lookupKey = computeLookupKeyForContact(db, contactId);
1872b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1873b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (lookupKey == null) {
1874b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mLookupKeyUpdate.bindNull(1);
1875b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
1876b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mLookupKeyUpdate.bindString(1, Uri.encode(lookupKey));
1877b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1878b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mLookupKeyUpdate.bindLong(2, contactId);
1879b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1880b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mLookupKeyUpdate.execute();
1881b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1882b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
18833a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    // Overridden by ProfileAggregator.
1884b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    protected String computeLookupKeyForContact(SQLiteDatabase db, long contactId) {
1885b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        StringBuilder sb = new StringBuilder();
1886b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mSelectionArgs1[0] = String.valueOf(contactId);
1887b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final Cursor c = db.query(LookupKeyQuery.TABLE, LookupKeyQuery.COLUMNS,
1888b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                RawContacts.CONTACT_ID + "=?", mSelectionArgs1, null, null, RawContacts._ID);
1889b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1890b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while (c.moveToNext()) {
1891b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                ContactLookupKey.appendToLookupKey(sb,
1892b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        c.getString(LookupKeyQuery.ACCOUNT_TYPE_AND_DATA_SET),
1893b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        c.getString(LookupKeyQuery.ACCOUNT_NAME),
1894b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        c.getLong(LookupKeyQuery.ID),
1895b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        c.getString(LookupKeyQuery.SOURCE_ID),
1896b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                        c.getString(LookupKeyQuery.DISPLAY_NAME));
1897b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1898b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1899b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            c.close();
1900b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1901b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return sb.length() == 0 ? null : sb.toString();
1902b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1903b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1904b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1905b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Execute {@link SQLiteStatement} that will update the
1906b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * {@link Contacts#STARRED} flag for the given {@link RawContacts#_ID}.
1907b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
19083a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updateStarred(long rawContactId) {
1909b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1910b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1911b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1912b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1913b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1914b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mStarredUpdate.bindLong(1, contactId);
1915b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mStarredUpdate.execute();
1916b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1917b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1918b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1919b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Execute {@link SQLiteStatement} that will update the
1920e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang     * {@link Contacts#SEND_TO_VOICEMAIL} flag for the given {@link RawContacts#_ID}.
1921e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang     */
1922e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang    public final void updateSendToVoicemail(long rawContactId) {
1923e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        long contactId = mDbHelper.getContactId(rawContactId);
1924e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        if (contactId == 0) {
1925e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang            return;
1926e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        }
1927e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang
1928e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        mSendToVoicemailUpdate.bindLong(1, contactId);
1929e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang        mSendToVoicemailUpdate.execute();
1930e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang    }
1931e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang
1932e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang    /**
1933e3443220edd7afdba6e5c4259232ad2b253fa1c8Tingting Wang     * Execute {@link SQLiteStatement} that will update the
1934b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * {@link Contacts#PINNED} flag for the given {@link RawContacts#_ID}.
1935b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
19363a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final void updatePinned(long rawContactId) {
1937b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        long contactId = mDbHelper.getContactId(rawContactId);
1938b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (contactId == 0) {
1939b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            return;
1940b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1941b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPinnedUpdate.bindLong(1, contactId);
1942b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        mPinnedUpdate.execute();
1943b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1944b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1945b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1946b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Finds matching contacts and returns a cursor on those.
1947b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
19483a83f4c60fbe7eb2ee31186d0675dcfbac3ee6b5Makoto Onuki    public final Cursor queryAggregationSuggestions(SQLiteQueryBuilder qb,
1949b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            String[] projection, long contactId, int maxSuggestions, String filter,
1950b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            ArrayList<AggregationSuggestionParameter> parameters) {
1951b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final SQLiteDatabase db = mDbHelper.getReadableDatabase();
1952b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        db.beginTransaction();
1953b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
1954b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            List<MatchScore> bestMatches = findMatchingContacts(db, contactId, parameters);
19555ca831dc33dfa16821647e14e377c484df823c80Zheng Fu            List<MatchScore> bestMatchesWithoutDuplicateContactIds = new ArrayList<>();
19565ca831dc33dfa16821647e14e377c484df823c80Zheng Fu            Set<Long> contactIds = new HashSet<>();
19575ca831dc33dfa16821647e14e377c484df823c80Zheng Fu            for (MatchScore bestMatch : bestMatches) {
19585ca831dc33dfa16821647e14e377c484df823c80Zheng Fu                long cid = bestMatch.getContactId();
195963a781313c71c966f9ca44a0cb3c13b477981f52Zheng Fu                if (!contactIds.contains(cid) && cid != contactId) {
19605ca831dc33dfa16821647e14e377c484df823c80Zheng Fu                    bestMatchesWithoutDuplicateContactIds.add(bestMatch);
19615ca831dc33dfa16821647e14e377c484df823c80Zheng Fu                    contactIds.add(cid);
19625ca831dc33dfa16821647e14e377c484df823c80Zheng Fu                }
19635ca831dc33dfa16821647e14e377c484df823c80Zheng Fu            }
19645ca831dc33dfa16821647e14e377c484df823c80Zheng Fu            return queryMatchingContacts(qb, db, projection, bestMatchesWithoutDuplicateContactIds,
19655ca831dc33dfa16821647e14e377c484df823c80Zheng Fu                    maxSuggestions, filter);
1966b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
1967b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            db.endTransaction();
1968b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1969b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1970b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1971b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private interface ContactIdQuery {
1972b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        String[] COLUMNS = new String[] {
1973b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            Contacts._ID
1974b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        };
1975b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1976b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int _ID = 0;
1977b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
1978b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1979b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
1980b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Loads contacts with specified IDs and returns them in the order of IDs in the
1981b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * supplied list.
1982b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
1983b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    private Cursor queryMatchingContacts(SQLiteQueryBuilder qb, SQLiteDatabase db,
1984b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            String[] projection, List<MatchScore> bestMatches, int maxSuggestions, String filter) {
1985b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        StringBuilder sb = new StringBuilder();
1986b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(Contacts._ID);
1987b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(" IN (");
1988b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (int i = 0; i < bestMatches.size(); i++) {
1989b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            MatchScore matchScore = bestMatches.get(i);
1990b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (i != 0) {
1991b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                sb.append(",");
1992b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
1993b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            sb.append(matchScore.getContactId());
1994b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
1995b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(")");
1996b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
1997b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (!TextUtils.isEmpty(filter)) {
1998b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            sb.append(" AND " + Contacts._ID + " IN ");
1999b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            mContactsProvider.appendContactFilterAsNestedQuery(sb, filter);
2000b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2001b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2002b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Run a query and find ids of best matching contacts satisfying the filter (if any)
2003b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        HashSet<Long> foundIds = new HashSet<Long>();
2004b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Cursor cursor = db.query(qb.getTables(), ContactIdQuery.COLUMNS, sb.toString(),
2005b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                null, null, null, null);
2006b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        try {
2007b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            while(cursor.moveToNext()) {
2008b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                foundIds.add(cursor.getLong(ContactIdQuery._ID));
2009b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
2010b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } finally {
2011b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            cursor.close();
2012b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2013b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2014b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Exclude all contacts that did not match the filter
2015b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Iterator<MatchScore> iter = bestMatches.iterator();
2016b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        while (iter.hasNext()) {
2017b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            long id = iter.next().getContactId();
2018b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (!foundIds.contains(id)) {
2019b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                iter.remove();
2020b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
2021b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2022b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2023b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Limit the number of returned suggestions
2024b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        final List<MatchScore> limitedMatches;
2025b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        if (bestMatches.size() > maxSuggestions) {
2026b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            limitedMatches = bestMatches.subList(0, maxSuggestions);
2027b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        } else {
2028b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            limitedMatches = bestMatches;
2029b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2030b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2031b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Build an in-clause with the remaining contact IDs
2032b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.setLength(0);
2033b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(Contacts._ID);
2034b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(" IN (");
2035b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (int i = 0; i < limitedMatches.size(); i++) {
2036b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            MatchScore matchScore = limitedMatches.get(i);
2037b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            if (i != 0) {
2038b94bfa502569ce869d443353c174d02754d42a82Zheng Fu                sb.append(",");
2039b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            }
2040b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            sb.append(matchScore.getContactId());
2041b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2042b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        sb.append(")");
2043b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2044b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Run the final query with the required projection and contact IDs found by the first query
2045b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        cursor = qb.query(db, projection, sb.toString(), null, null, null, Contacts._ID);
2046b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2047b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Build a sorted list of discovered IDs
2048b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        ArrayList<Long> sortedContactIds = new ArrayList<Long>(limitedMatches.size());
2049b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (MatchScore matchScore : limitedMatches) {
2050b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            sortedContactIds.add(matchScore.getContactId());
2051b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2052b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2053b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        Collections.sort(sortedContactIds);
2054b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2055b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        // Map cursor indexes according to the descending order of match scores
2056b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        int[] positionMap = new int[limitedMatches.size()];
2057b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        for (int i = 0; i < positionMap.length; i++) {
2058b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            long id = limitedMatches.get(i).getContactId();
2059b94bfa502569ce869d443353c174d02754d42a82Zheng Fu            positionMap[i] = sortedContactIds.indexOf(id);
2060b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        }
2061b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2062b94bfa502569ce869d443353c174d02754d42a82Zheng Fu        return new ReorderingCursorWrapper(cursor, positionMap);
2063b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    }
2064b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2065b94bfa502569ce869d443353c174d02754d42a82Zheng Fu    /**
2066b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * Finds contacts with data matches and returns a list of {@link MatchScore}'s in the
2067b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * descending order of match score.
2068b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     * @param parameters
2069b94bfa502569ce869d443353c174d02754d42a82Zheng Fu     */
2070aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    protected abstract List<MatchScore> findMatchingContacts(final SQLiteDatabase db,
2071aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu            long contactId, ArrayList<AggregationSuggestionParameter> parameters);
2072b94bfa502569ce869d443353c174d02754d42a82Zheng Fu
2073aa18c233fdec3359c5231d4a5f61188446bf5d6fZheng Fu    public abstract void updateAggregationAfterVisibilityChange(long contactId);
2074b94bfa502569ce869d443353c174d02754d42a82Zheng Fu}
2075