1c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan/*
2c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * Copyright (C) 2011 The Android Open Source Project
3c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan *
4c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * Licensed under the Apache License, Version 2.0 (the "License");
5c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * you may not use this file except in compliance with the License.
6c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * You may obtain a copy of the License at
7c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan *
8c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan *      http://www.apache.org/licenses/LICENSE-2.0
9c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan *
10c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * Unless required by applicable law or agreed to in writing, software
11c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * distributed under the License is distributed on an "AS IS" BASIS,
12c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * See the License for the specific language governing permissions and
14c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * limitations under the License.
15c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan */
16c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanpackage com.android.contacts.group;
17c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
18c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.content.ContentResolver;
19c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.content.Context;
20c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.database.Cursor;
21c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.graphics.Bitmap;
22c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.graphics.BitmapFactory;
23c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Email;
24c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Phone;
25c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.CommonDataKinds.Photo;
26c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.Contacts.Data;
27c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.RawContacts;
28c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.provider.ContactsContract.RawContactsEntity;
29c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.text.TextUtils;
30c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.view.LayoutInflater;
31c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.view.View;
32c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.view.ViewGroup;
33c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.widget.ArrayAdapter;
34c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.widget.AutoCompleteTextView;
35c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.widget.Filter;
36c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.widget.ImageView;
37c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport android.widget.TextView;
38c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
39e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R;
40f677450d82e7b961fda2a18fea2ad51e88437e8bYorke Leeimport com.android.contacts.common.ContactPhotoManager;
41e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
42e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
43c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport java.util.ArrayList;
44c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport java.util.Arrays;
45c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport java.util.HashMap;
46c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanimport java.util.List;
47c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
48c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan/**
49c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * This adapter provides suggested contacts that can be added to a group for an
50c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan * {@link AutoCompleteTextView} within the group editor.
51c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan */
52c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuanpublic class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> {
53c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
54c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final String[] PROJECTION_FILTERED_MEMBERS = new String[] {
55c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        RawContacts._ID,                        // 0
56c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        RawContacts.CONTACT_ID,                 // 1
57c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        RawContacts.DISPLAY_NAME_PRIMARY        // 2
58c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    };
59c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
60c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int RAW_CONTACT_ID_COLUMN_INDEX = 0;
61c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int CONTACT_ID_COLUMN_INDEX = 1;
62c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 2;
63c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
64c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final String[] PROJECTION_MEMBER_DATA = new String[] {
65c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        RawContacts._ID,                        // 0
66c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        RawContacts.CONTACT_ID,                 // 1
67c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        Data.MIMETYPE,                          // 2
68c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        Data.DATA1,                             // 3
69c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        Photo.PHOTO,                            // 4
70c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    };
71c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
72c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int MIMETYPE_COLUMN_INDEX = 2;
73c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int DATA_COLUMN_INDEX = 3;
74c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int PHOTO_COLUMN_INDEX = 4;
75c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
76c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private Filter mFilter;
77c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private ContentResolver mContentResolver;
78c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private LayoutInflater mInflater;
79c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
80f75900ed2bafa5411c46e8fde0b8dccbc6753176Makoto Onuki    private String mAccountType;
81f75900ed2bafa5411c46e8fde0b8dccbc6753176Makoto Onuki    private String mAccountName;
82f75900ed2bafa5411c46e8fde0b8dccbc6753176Makoto Onuki    private String mDataSet;
83c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
842d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan    // TODO: Make this a Map for better performance when we check if a new contact is in the list
852d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan    // or not
86f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki    private final List<Long> mExistingMemberContactIds = new ArrayList<Long>();
87c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
88c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    private static final int SUGGESTIONS_LIMIT = 5;
89c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
90c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public SuggestedMemberListAdapter(Context context, int textViewResourceId) {
91c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        super(context, textViewResourceId);
92c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
93c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
94c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
95c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public void setAccountType(String accountType) {
96c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        mAccountType = accountType;
97c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
98c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
99c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public void setAccountName(String accountName) {
100c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        mAccountName = accountName;
101c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
102c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
1032b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    public void setDataSet(String dataSet) {
1042b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro        mDataSet = dataSet;
1052b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro    }
1062b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
107c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public void setContentResolver(ContentResolver resolver) {
108c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        mContentResolver = resolver;
109c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
110c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
111f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki    public void updateExistingMembersList(List<GroupEditorFragment.Member> list) {
112f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki        mExistingMemberContactIds.clear();
113f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki        for (GroupEditorFragment.Member member : list) {
114f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki            mExistingMemberContactIds.add(member.getContactId());
115f81b6d9ba129f0220de93aa9a7426547889755c3Makoto Onuki        }
116c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
117c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
118c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public void addNewMember(long contactId) {
119c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        mExistingMemberContactIds.add(contactId);
120c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
121c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
122c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public void removeMember(long contactId) {
123c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        if (mExistingMemberContactIds.contains(contactId)) {
124c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mExistingMemberContactIds.remove(contactId);
125c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
126c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
127c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
128c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    @Override
129c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public View getView(int position, View convertView, ViewGroup parent) {
130c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        View result = convertView;
131c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        if (result == null) {
132c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            result = mInflater.inflate(R.layout.group_member_suggestion, parent, false);
133c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
134c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        // TODO: Use a viewholder
135c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        SuggestedMember member = getItem(position);
136c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        TextView text1 = (TextView) result.findViewById(R.id.text1);
137c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        TextView text2 = (TextView) result.findViewById(R.id.text2);
138c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        ImageView icon = (ImageView) result.findViewById(R.id.icon);
139c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        text1.setText(member.getDisplayName());
140c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        if (member.hasExtraInfo()) {
141c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            text2.setText(member.getExtraInfo());
142c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        } else {
143c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            text2.setVisibility(View.GONE);
144c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
145c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        byte[] byteArray = member.getPhotoByteArray();
146c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        if (byteArray == null) {
147f677450d82e7b961fda2a18fea2ad51e88437e8bYorke Lee            icon.setImageDrawable(ContactPhotoManager.getDefaultAvatarDrawableForContact(
148f677450d82e7b961fda2a18fea2ad51e88437e8bYorke Lee                    icon.getResources(), false, null));
14959b46720e92799a29d84e6e31fb3eeea60166f0dMakoto Onuki        } else {
150c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
151c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            icon.setImageBitmap(bitmap);
152c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
15359b46720e92799a29d84e6e31fb3eeea60166f0dMakoto Onuki        result.setTag(member);
154c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        return result;
155c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
156c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
157c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    @Override
158c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public Filter getFilter() {
159c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        if (mFilter == null) {
160c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mFilter = new SuggestedMemberFilter();
161c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
162c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        return mFilter;
163c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
164c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
165c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    /**
166c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan     * This filter queries for raw contacts that match the given account name and account type,
167c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan     * as well as the search query.
168c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan     */
169c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public class SuggestedMemberFilter extends Filter {
170c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
171c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        @Override
172c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        protected FilterResults performFiltering(CharSequence prefix) {
173c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            FilterResults results = new FilterResults();
174c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            if (mContentResolver == null || TextUtils.isEmpty(prefix)) {
175c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                return results;
176c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
177c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
178de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            // Create a list to store the suggested contacts (which will be alphabetically ordered),
179de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            // but also keep a map of raw contact IDs to {@link SuggestedMember}s to make it easier
180de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            // to add supplementary data to the contact (photo, phone, email) to the members based
181de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            // on raw contact IDs after the second query is completed.
182de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            List<SuggestedMember> suggestionsList = new ArrayList<SuggestedMember>();
183c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            HashMap<Long, SuggestedMember> suggestionsMap = new HashMap<Long, SuggestedMember>();
184c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
185c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // First query for all the raw contacts that match the given search query
186c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // and have the same account name and type as specified in this adapter
187c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            String searchQuery = prefix.toString() + "%";
1882b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            String accountClause = RawContacts.ACCOUNT_NAME + "=? AND " +
1892b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                    RawContacts.ACCOUNT_TYPE + "=?";
1902b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            String[] args;
1912b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            if (mDataSet == null) {
1922b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                accountClause += " AND " + RawContacts.DATA_SET + " IS NULL";
1932b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                args = new String[] {mAccountName, mAccountType, searchQuery, searchQuery};
1942b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            } else {
1952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                accountClause += " AND " + RawContacts.DATA_SET + "=?";
1962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                args = new String[] {
1972b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                        mAccountName, mAccountType, mDataSet, searchQuery, searchQuery
1982b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                };
1992b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            }
2002b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro
201c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            Cursor cursor = mContentResolver.query(
202c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    RawContacts.CONTENT_URI, PROJECTION_FILTERED_MEMBERS,
2032b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                    accountClause + " AND (" +
204c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    RawContacts.DISPLAY_NAME_PRIMARY + " LIKE ? OR " +
205c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    RawContacts.DISPLAY_NAME_ALTERNATIVE + " LIKE ? )",
206de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                    args, RawContacts.DISPLAY_NAME_PRIMARY + " COLLATE LOCALIZED ASC");
207c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
208c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            if (cursor == null) {
209c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                return results;
210c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
211c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
212c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Read back the results from the cursor and filter out existing group members.
213c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // For valid suggestions, add them to the hash map of suggested members.
214c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            try {
215c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                cursor.moveToPosition(-1);
216c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                while (cursor.moveToNext() && suggestionsMap.keySet().size() < SUGGESTIONS_LIMIT) {
2172d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan                    long rawContactId = cursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
218c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
219c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    // Filter out contacts that have already been added to this group
220c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    if (mExistingMemberContactIds.contains(contactId)) {
221c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                        continue;
222c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    }
223c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    // Otherwise, add the contact as a suggested new group member
224c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    String displayName = cursor.getString(DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
225de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                    SuggestedMember member = new SuggestedMember(rawContactId, displayName,
226de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                            contactId);
227de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                    // Store the member in the list of suggestions and add it to the hash map too.
228de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                    suggestionsList.add(member);
229de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan                    suggestionsMap.put(rawContactId, member);
230c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                }
231c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            } finally {
232c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                cursor.close();
233c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
234c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
235c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            int numSuggestions = suggestionsMap.keySet().size();
236c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            if (numSuggestions == 0) {
237c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                return results;
238c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
239c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
240c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Create a part of the selection string for the next query with the pattern (?, ?, ?)
241c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // where the number of comma-separated question marks represent the number of raw
242c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // contact IDs found in the previous query (while respective the SUGGESTION_LIMIT)
243c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            final StringBuilder rawContactIdSelectionBuilder = new StringBuilder();
244c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            final String[] questionMarks = new String[numSuggestions];
245c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            Arrays.fill(questionMarks, "?");
246c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            rawContactIdSelectionBuilder.append(RawContacts._ID + " IN (")
247c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    .append(TextUtils.join(",", questionMarks))
248c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    .append(")");
249c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
250c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Construct the selection args based on the raw contact IDs we're interested in
251c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // (as well as the photo, email, and phone mimetypes)
252c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            List<String> selectionArgs = new ArrayList<String>();
253c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            selectionArgs.add(Photo.CONTENT_ITEM_TYPE);
254c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            selectionArgs.add(Email.CONTENT_ITEM_TYPE);
255c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            selectionArgs.add(Phone.CONTENT_ITEM_TYPE);
256c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            for (Long rawContactId : suggestionsMap.keySet()) {
257c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                selectionArgs.add(String.valueOf(rawContactId));
258c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
259c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
260c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Perform a second query to retrieve a photo and possibly a phone number or email
261c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // address for the suggested contact
262c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            Cursor memberDataCursor = mContentResolver.query(
263c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    RawContactsEntity.CONTENT_URI, PROJECTION_MEMBER_DATA,
264c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    "(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=? OR " + Data.MIMETYPE +
265c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    "=?) AND " + rawContactIdSelectionBuilder.toString(),
266c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    selectionArgs.toArray(new String[0]), null);
267c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
26813c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner            if (memberDataCursor != null) {
26913c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                try {
27013c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                    memberDataCursor.moveToPosition(-1);
27113c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                    while (memberDataCursor.moveToNext()) {
27213c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        long rawContactId = memberDataCursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
27313c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        SuggestedMember member = suggestionsMap.get(rawContactId);
27413c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        if (member == null) {
27513c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            continue;
27613c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        }
27713c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        String mimetype = memberDataCursor.getString(MIMETYPE_COLUMN_INDEX);
27813c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
27913c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            // Set photo
28013c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            byte[] bitmapArray = memberDataCursor.getBlob(PHOTO_COLUMN_INDEX);
28113c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            member.setPhotoByteArray(bitmapArray);
28213c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                        } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype) ||
28313c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                                Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
28413c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            // Set at most 1 extra piece of contact info that can be a phone number or
28513c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            // email
28613c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            if (!member.hasExtraInfo()) {
28713c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                                String info = memberDataCursor.getString(DATA_COLUMN_INDEX);
28813c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                                member.setExtraInfo(info);
28913c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                            }
290c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                        }
291c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                    }
29213c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                } finally {
29313c42f4ebb1c7f31750262946ce3b390c93d5320Jay Shrauner                    memberDataCursor.close();
294c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                }
295c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
296de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            results.values = suggestionsList;
297c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return results;
298c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
299c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
300c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        @Override
301c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        protected void publishResults(CharSequence constraint, FilterResults results) {
30218958a29b3eddb6fc42cf651ec0eed27103f534dDaniel Lehmann            @SuppressWarnings("unchecked")
303de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            List<SuggestedMember> suggestionsList = (List<SuggestedMember>) results.values;
304de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            if (suggestionsList == null) {
305c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                return;
306c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
307c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
308c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Clear out the existing suggestions in this adapter
309c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            clear();
310c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
311c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            // Add all the suggested members to this adapter
312de4e404bd88cb60a8286e17383a49534efcc0e56Katherine Kuan            for (SuggestedMember member : suggestionsList) {
313c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan                add(member);
314c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            }
315c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
316c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            notifyDataSetChanged();
317c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
318c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
319c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
320c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    /**
321c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan     * This represents a single contact that is a suggestion for the user to add to a group.
322c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan     */
3232d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan    // TODO: Merge this with the {@link GroupEditorFragment} Member class once we can find the
3242d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan    // lookup URI for this contact using the autocomplete filter queries
325c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    public class SuggestedMember {
326c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
3272d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan        private long mRawContactId;
328c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        private long mContactId;
329c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        private String mDisplayName;
330c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        private String mExtraInfo;
331c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        private byte[] mPhoto;
332c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
3332d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan        public SuggestedMember(long rawContactId, String displayName, long contactId) {
3342d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan            mRawContactId = rawContactId;
335c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mDisplayName = displayName;
336c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mContactId = contactId;
337c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
338c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
339c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public String getDisplayName() {
340c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return mDisplayName;
341c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
342c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
343c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public String getExtraInfo() {
344c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return mExtraInfo;
345c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
346c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
3472d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan        public long getRawContactId() {
3482d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan            return mRawContactId;
3492d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan        }
3502d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan
351c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public long getContactId() {
352c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return mContactId;
353c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
354c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
355c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public byte[] getPhotoByteArray() {
356c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return mPhoto;
357c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
358c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
359c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public boolean hasExtraInfo() {
360c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            return mExtraInfo != null;
361c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
362c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
363c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        /**
364c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan         * Set a phone number or email to distinguish this contact
365c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan         */
366c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public void setExtraInfo(String info) {
367c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mExtraInfo = info;
368c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
369c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan
370c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        public void setPhotoByteArray(byte[] photo) {
371c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan            mPhoto = photo;
372c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan        }
3736ae983fc9fff8ec820fd09d0d5b997bcbea31a43Makoto Onuki
3746ae983fc9fff8ec820fd09d0d5b997bcbea31a43Makoto Onuki        @Override
3756ae983fc9fff8ec820fd09d0d5b997bcbea31a43Makoto Onuki        public String toString() {
3766ae983fc9fff8ec820fd09d0d5b997bcbea31a43Makoto Onuki            return getDisplayName();
3776ae983fc9fff8ec820fd09d0d5b997bcbea31a43Makoto Onuki        }
378c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan    }
379c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan}
380