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