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