AggregationSuggestionEngine.java revision f491ae914769f1062a4d9c31fcbec5ff0e01e4e6
1f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov/* 2f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * Copyright (C) 2010 The Android Open Source Project 3f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * 4f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * Licensed under the Apache License, Version 2.0 (the "License"); 5f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * you may not use this file except in compliance with the License. 6f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * You may obtain a copy of the License at 7f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * 8f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * http://www.apache.org/licenses/LICENSE-2.0 9f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * 10f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * Unless required by applicable law or agreed to in writing, software 11f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * distributed under the License is distributed on an "AS IS" BASIS, 12f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * See the License for the specific language governing permissions and 14f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * limitations under the License 15f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov */ 16f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 17f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovpackage com.android.contacts.views.editor; 18f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 19f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport com.android.contacts.model.EntityDelta.ValuesDelta; 20f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport com.google.android.collect.Lists; 21f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 22f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.content.ContentResolver; 23f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.content.Context; 24f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.database.Cursor; 25f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.net.Uri; 26f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.os.Handler; 27f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.os.HandlerThread; 28f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.os.Message; 29f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.os.Process; 30f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Email; 31f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Nickname; 32f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone; 33f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Photo; 34f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 35f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.Contacts; 36f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions; 37f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.Contacts.AggregationSuggestions.Builder; 38f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.provider.ContactsContract.Data; 39f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport android.text.TextUtils; 40f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 41f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport java.util.ArrayList; 42f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport java.util.Arrays; 43f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovimport java.util.List; 44f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 45f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov/** 46f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov * Runs asynchronous queries to obtain aggregation suggestions in the as-you-type mode. 47f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov */ 48f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikovpublic class AggregationSuggestionEngine extends HandlerThread { 49f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final String TAG = "AggregationSuggestionEngine"; 50f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 51f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private static final int MESSAGE_NAME_CHANGE = 1; 52f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private static final int MESSAGE_DATA_CURSOR = 2; 53f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 54f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private static final long SUGGESTION_LOOKUP_DELAY_MILLIS = 300; 55f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 56f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private static final int MAX_SUGGESTION_COUNT = 3; 57f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 58f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private final Context mContext; 59f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 60f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private long[] mSuggestedContactIds = new long[0]; 61f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 62f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private Handler mMainHandler; 63f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private Handler mHandler; 64f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private long mContactId; 65f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private Listener mListener; 66f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private Cursor mDataCursor; 67f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 68f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public interface Listener { 69f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov void onAggregationSuggestionChange(); 70f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 71f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 72f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final class Suggestion { 73f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public long contactId; 74f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public List<Long> rawContactIds; 75f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String lookupKey; 76f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String name; 77f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String phoneNumber; 78f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String emailAddress; 79f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String nickname; 80f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public byte[] photo; 81f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 82f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov @Override 83f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public String toString() { 84f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return "ID: " + contactId + " rawContactIds: " + rawContactIds + " name: " + name 85f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + " phone: " + phoneNumber + " email: " + emailAddress + " nickname: " 86f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + nickname + (photo != null ? " [has photo]" : ""); 87f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 88f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 89f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 90f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public AggregationSuggestionEngine(Context context) { 91f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov super("AggregationSuggestions", Process.THREAD_PRIORITY_BACKGROUND); 92f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mContext = context; 93f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mMainHandler = new Handler() { 94f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov @Override 95f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public void handleMessage(Message msg) { 96f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov AggregationSuggestionEngine.this.deliverNotification((Cursor) msg.obj); 97f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 98f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov }; 99f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 100f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 101f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov protected Handler getHandler() { 102f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (mHandler == null) { 103f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mHandler = new Handler(getLooper()) { 104f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov @Override 105f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public void handleMessage(Message msg) { 106f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov AggregationSuggestionEngine.this.handleMessage(msg); 107f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 108f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov }; 109f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 110f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return mHandler; 111f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 112f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 113f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public void setContactId(long contactId) { 114f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mContactId = contactId; 115f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 116f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 117f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public void setListener(Listener listener) { 118f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mListener = listener; 119f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 120f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 121f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov @Override 122f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public boolean quit() { 123f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (mDataCursor != null) { 124f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mDataCursor.close(); 125f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 126f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mDataCursor = null; 127f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return super.quit(); 128f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 129f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 130f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public void onNameChange(ValuesDelta values) { 131f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Handler handler = getHandler(); 132f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov handler.removeMessages(MESSAGE_NAME_CHANGE); 133f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 134f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Uri uri = buildAggregationSuggestionUri(values); 135f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (uri == null) { 136f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return; 137f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 138f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 139f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Message msg = handler.obtainMessage(MESSAGE_NAME_CHANGE, uri); 140f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov handler.sendMessageDelayed(msg, SUGGESTION_LOOKUP_DELAY_MILLIS); 141f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 142f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 143f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private Uri buildAggregationSuggestionUri(ValuesDelta values) { 144f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov StringBuilder nameSb = new StringBuilder(); 145f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(nameSb, values, StructuredName.PREFIX); 146f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(nameSb, values, StructuredName.GIVEN_NAME); 147f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(nameSb, values, StructuredName.MIDDLE_NAME); 148f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(nameSb, values, StructuredName.FAMILY_NAME); 149f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(nameSb, values, StructuredName.SUFFIX); 150f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 151f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov StringBuilder phoneticNameSb = new StringBuilder(); 152f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(phoneticNameSb, values, StructuredName.PHONETIC_FAMILY_NAME); 153f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(phoneticNameSb, values, StructuredName.PHONETIC_MIDDLE_NAME); 154f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov appendValue(phoneticNameSb, values, StructuredName.PHONETIC_GIVEN_NAME); 155f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 156f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (nameSb.length() == 0 && phoneticNameSb.length() == 0) { 157f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return null; 158f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 159f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 160f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Builder builder = AggregationSuggestions.builder() 161f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov .setLimit(MAX_SUGGESTION_COUNT) 162f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov .setContactId(mContactId); 163f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 164f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (nameSb.length() != 0) { 165f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov builder.addParameter(AggregationSuggestions.PARAMETER_MATCH_NAME, nameSb.toString()); 166f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 167f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 168f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (phoneticNameSb.length() != 0) { 169f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov builder.addParameter( 170f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov AggregationSuggestions.PARAMETER_MATCH_NAME, phoneticNameSb.toString()); 171f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 172f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 173f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return builder.build(); 174f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 175f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 176f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private void appendValue(StringBuilder sb, ValuesDelta values, String column) { 177f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov String value = values.getAsString(column); 178f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!TextUtils.isEmpty(value)) { 179f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (sb.length() > 0) { 180f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.append(' '); 181f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 182f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.append(value); 183f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 184f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 185f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 186f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov protected void handleMessage(Message msg) { 187f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov switch(msg.what) { 188f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov case MESSAGE_NAME_CHANGE: 189f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov loadAggregationSuggestions((Uri) msg.obj); 190f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov break; 191f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 192f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 193f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 194f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private static final class DataQuery { 195f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 196f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final String SELECTION_PREFIX = 197f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.MIMETYPE + " IN ('" 198f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + Phone.CONTENT_ITEM_TYPE + "','" 199f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + Email.CONTENT_ITEM_TYPE + "','" 200f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + StructuredName.CONTENT_ITEM_TYPE + "','" 201f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + Nickname.CONTENT_ITEM_TYPE + "','" 202f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + Photo.CONTENT_ITEM_TYPE + "')" 203f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov + " AND " + Data.CONTACT_ID + " IN ("; 204f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 205f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final String[] COLUMNS = { 206f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data._ID, 207f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.CONTACT_ID, 208f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.LOOKUP_KEY, 209f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.PHOTO_ID, 210f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.DISPLAY_NAME, 211f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.RAW_CONTACT_ID, 212f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.MIMETYPE, 213f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.DATA1, 214f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Data.IS_SUPER_PRIMARY, 215f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Photo.PHOTO, 216f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov }; 217f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 218f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int ID = 0; 219f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int CONTACT_ID = 1; 220f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int LOOKUP_KEY = 2; 221f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int PHOTO_ID = 3; 222f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int DISPLAY_NAME = 4; 223f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int RAW_CONTACT_ID = 5; 224f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int MIMETYPE = 6; 225f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int DATA1 = 7; 226f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int IS_SUPERPRIMARY = 8; 227f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public static final int PHOTO = 9; 228f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 229f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 230f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private void loadAggregationSuggestions(Uri uri) { 231f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov ContentResolver contentResolver = mContext.getContentResolver(); 232f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Cursor cursor = contentResolver.query(uri, new String[]{Contacts._ID}, null, null, null); 233f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov try { 234f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov // If a new request is pending, chuck the result of the previous request 235f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (getHandler().hasMessages(MESSAGE_NAME_CHANGE)) { 236f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return; 237f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 238f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 239f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov boolean changed = updateSuggestedContactIds(cursor); 240f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!changed) { 241f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return; 242f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 243f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 244f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov StringBuilder sb = new StringBuilder(DataQuery.SELECTION_PREFIX); 245f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov int count = mSuggestedContactIds.length; 246f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov for (int i = 0; i < count; i++) { 247f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (i > 0) { 248f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.append(','); 249f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 250f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.append(mSuggestedContactIds[i]); 251f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 252f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.append(')'); 253f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov sb.toString(); 254f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 255f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Cursor dataCursor = contentResolver.query(Data.CONTENT_URI, 256f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov DataQuery.COLUMNS, sb.toString(), null, Data.CONTACT_ID); 257f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mMainHandler.sendMessage(mMainHandler.obtainMessage(MESSAGE_DATA_CURSOR, dataCursor)); 258f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } finally { 259f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov cursor.close(); 260f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 261f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 262f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 263f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov private boolean updateSuggestedContactIds(Cursor cursor) { 264f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov int count = cursor.getCount(); 265f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov boolean changed = count != mSuggestedContactIds.length; 266f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!changed) { 267f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov while (cursor.moveToNext()) { 268f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov long contactId = cursor.getLong(0); 269f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (Arrays.binarySearch(mSuggestedContactIds, contactId) < 0) { 270f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov changed = true; 271f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov break; 272f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 273f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 274f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 275f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 276f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (changed) { 277f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mSuggestedContactIds = new long[count]; 278f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov cursor.moveToPosition(-1); 279f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov for (int i = 0; i < count; i++) { 280f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov cursor.moveToNext(); 281f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mSuggestedContactIds[i] = cursor.getLong(0); 282f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 283f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Arrays.sort(mSuggestedContactIds); 284f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 285f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 286f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return changed; 287f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 288f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 289f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov protected void deliverNotification(Cursor dataCursor) { 290f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (mDataCursor != null) { 291f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mDataCursor.close(); 292f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 293f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mDataCursor = dataCursor; 294f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (mListener != null) { 295f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mListener.onAggregationSuggestionChange(); 296f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 297f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 298f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 299f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public int getSuggestedContactCount() { 300f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return mSuggestedContactIds.length; 301f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 302f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 303f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov public List<Suggestion> getSuggestions() { 304f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov ArrayList<Suggestion> list = Lists.newArrayList(); 305f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (mDataCursor != null) { 306f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Suggestion suggestion = null; 307f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov long currentContactId = -1; 308f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov mDataCursor.moveToPosition(-1); 309f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov while (mDataCursor.moveToNext()) { 310f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov long contactId = mDataCursor.getLong(DataQuery.CONTACT_ID); 311f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (contactId != currentContactId) { 312f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion = new Suggestion(); 313f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.contactId = contactId; 314f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.name = mDataCursor.getString(DataQuery.DISPLAY_NAME); 315f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.rawContactIds = Lists.newArrayList(); 316f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov list.add(suggestion); 317f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov currentContactId = contactId; 318f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 319f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 320f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov Long rawContactId = Long.valueOf(mDataCursor.getLong(DataQuery.RAW_CONTACT_ID)); 321f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!suggestion.rawContactIds.contains(rawContactId)) { 322f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.rawContactIds.add(rawContactId); 323f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 324f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov 325f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov String mimetype = mDataCursor.getString(DataQuery.MIMETYPE); 326f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (Phone.CONTENT_ITEM_TYPE.equals(mimetype)) { 327f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov String data = mDataCursor.getString(DataQuery.DATA1); 328f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov int superprimary = mDataCursor.getInt(DataQuery.IS_SUPERPRIMARY); 329f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!TextUtils.isEmpty(data) 330f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov && (superprimary != 0 || suggestion.phoneNumber == null)) { 331f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.phoneNumber = data; 332f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 333f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype)) { 334f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov String data = mDataCursor.getString(DataQuery.DATA1); 335f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov int superprimary = mDataCursor.getInt(DataQuery.IS_SUPERPRIMARY); 336f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!TextUtils.isEmpty(data) 337f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov && (superprimary != 0 || suggestion.emailAddress == null)) { 338f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.emailAddress = data; 339f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 340f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } else if (Nickname.CONTENT_ITEM_TYPE.equals(mimetype)) { 341f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov String data = mDataCursor.getString(DataQuery.DATA1); 342f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (!TextUtils.isEmpty(data)) { 343f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.nickname = data; 344f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 345f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } else if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) { 346f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov long dataId = mDataCursor.getLong(DataQuery.ID); 347f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov long photoId = mDataCursor.getLong(DataQuery.PHOTO_ID); 348f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov if (dataId == photoId && !mDataCursor.isNull(DataQuery.PHOTO)) { 349f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov suggestion.photo = mDataCursor.getBlob(DataQuery.PHOTO); 350f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 351f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 352f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 353f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 354f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov return list; 355f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov } 356f491ae914769f1062a4d9c31fcbec5ff0e01e4e6Dmitri Plotnikov} 357