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