RecipientAlternatesAdapter.java revision 00adb32f3cea49ec82467c0e1a9e42659b556836
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.ex.chips;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.provider.ContactsContract.DisplayNameSources;
22import android.text.util.Rfc822Token;
23import android.text.util.Rfc822Tokenizer;
24import android.util.Log;
25import android.view.LayoutInflater;
26import android.view.View;
27import android.view.ViewGroup;
28import android.widget.CursorAdapter;
29import android.widget.ImageView;
30import android.widget.TextView;
31
32import com.android.ex.chips.Queries.Query;
33import java.util.HashMap;
34
35/**
36 * RecipientAlternatesAdapter backs the RecipientEditTextView for managing contacts
37 * queried by email or by phone number.
38 */
39public class RecipientAlternatesAdapter extends CursorAdapter {
40    static final int MAX_LOOKUPS = 50;
41    private final LayoutInflater mLayoutInflater;
42
43    private final long mCurrentId;
44
45    private int mCheckedItemPosition = -1;
46
47    private OnCheckedItemChangedListener mCheckedItemChangedListener;
48
49    private static final String TAG = "RecipAlternates";
50
51    public static final int QUERY_TYPE_EMAIL = 0;
52    public static final int QUERY_TYPE_PHONE = 1;
53    private Query mQuery;
54
55    public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context,
56            String[] inAddresses) {
57        return getMatchingRecipients(context, inAddresses, QUERY_TYPE_EMAIL);
58    }
59
60    /**
61     * Get a HashMap of address to RecipientEntry that contains all contact
62     * information for a contact with the provided address, if one exists. This
63     * may block the UI, so run it in an async task.
64     *
65     * @param context Context.
66     * @param inAddresses Array of addresses on which to perform the lookup.
67     * @return HashMap<String,RecipientEntry>
68     */
69    public static HashMap<String, RecipientEntry> getMatchingRecipients(Context context,
70            String[] inAddresses, int addressType) {
71        Queries.Query query;
72        if (addressType == QUERY_TYPE_EMAIL) {
73            query = Queries.EMAIL;
74        } else {
75            query = Queries.PHONE;
76        }
77        int addressesSize = Math.min(MAX_LOOKUPS, inAddresses.length);
78        String[] addresses = new String[addressesSize];
79        StringBuilder bindString = new StringBuilder();
80        // Create the "?" string and set up arguments.
81        for (int i = 0; i < addressesSize; i++) {
82            Rfc822Token[] tokens = Rfc822Tokenizer.tokenize(inAddresses[i].toLowerCase());
83            addresses[i] = (tokens.length > 0 ? tokens[0].getAddress() : inAddresses[i]);
84            bindString.append("?");
85            if (i < addressesSize - 1) {
86                bindString.append(",");
87            }
88        }
89
90        if (Log.isLoggable(TAG, Log.DEBUG)) {
91            Log.d(TAG, "Doing reverse lookup for " + addresses.toString());
92        }
93
94        HashMap<String, RecipientEntry> recipientEntries = new HashMap<String, RecipientEntry>();
95        Cursor c = context.getContentResolver().query(
96                query.getContentUri(), query.getProjection(),
97                Queries.Query.DESTINATION + " IN (" + bindString.toString() + ")",
98                addresses,
99                null);
100
101        if (c != null) {
102            try {
103                if (c.moveToFirst()) {
104                    do {
105                        String address = c.getString(Queries.Query.DESTINATION);
106                        recipientEntries.put(address, RecipientEntry.constructTopLevelEntry(
107                                c.getString(Queries.Query.NAME),
108                                c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
109                                c.getString(Queries.Query.DESTINATION),
110                                c.getInt(Queries.Query.DESTINATION_TYPE),
111                                c.getString(Queries.Query.DESTINATION_LABEL),
112                                c.getLong(Queries.Query.CONTACT_ID),
113                                c.getLong(Queries.Query.DATA_ID),
114                                c.getString(Queries.Query.PHOTO_THUMBNAIL_URI)));
115                        if (Log.isLoggable(TAG, Log.DEBUG)) {
116                            Log.d(TAG, "Received reverse look up information for " + address
117                                    + " RESULTS: "
118                                    + " NAME : " + c.getString(Queries.Query.NAME)
119                                    + " CONTACT ID : " + c.getLong(Queries.Query.CONTACT_ID)
120                                    + " ADDRESS :" + c.getString(Queries.Query.DESTINATION));
121                        }
122                    } while (c.moveToNext());
123                }
124            } finally {
125                c.close();
126            }
127        }
128        return recipientEntries;
129    }
130
131    public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int viewId,
132            OnCheckedItemChangedListener listener) {
133        this(context, contactId, currentId, viewId, QUERY_TYPE_EMAIL, listener);
134    }
135
136    public RecipientAlternatesAdapter(Context context, long contactId, long currentId, int viewId,
137            int queryMode, OnCheckedItemChangedListener listener) {
138        super(context, getCursorForConstruction(context, contactId, queryMode), 0);
139        mLayoutInflater = LayoutInflater.from(context);
140        mCurrentId = currentId;
141        mCheckedItemChangedListener = listener;
142
143        if (queryMode == QUERY_TYPE_EMAIL) {
144            mQuery = Queries.EMAIL;
145        } else if (queryMode == QUERY_TYPE_PHONE) {
146            mQuery = Queries.PHONE;
147        } else {
148            mQuery = Queries.EMAIL;
149            Log.e(TAG, "Unsupported query type: " + queryMode);
150        }
151    }
152
153    private static Cursor getCursorForConstruction(Context context, long contactId, int queryType) {
154        if (queryType == QUERY_TYPE_EMAIL) {
155            return context.getContentResolver().query(
156                    Queries.EMAIL.getContentUri(),
157                    Queries.EMAIL.getProjection(),
158                    Queries.EMAIL.getProjection()[Queries.Query.CONTACT_ID] + " =?", new String[] {
159                        String.valueOf(contactId)
160                    }, null);
161        } else {
162            return context.getContentResolver().query(
163                    Queries.PHONE.getContentUri(),
164                    Queries.PHONE.getProjection(),
165                    Queries.PHONE.getProjection()[Queries.Query.CONTACT_ID] + " =?", new String[] {
166                        String.valueOf(contactId)
167                    }, null);
168        }
169    }
170
171    @Override
172    public long getItemId(int position) {
173        Cursor c = getCursor();
174        if (c.moveToPosition(position)) {
175            c.getLong(Queries.Query.DATA_ID);
176        }
177        return -1;
178    }
179
180    public RecipientEntry getRecipientEntry(int position) {
181        Cursor c = getCursor();
182        c.moveToPosition(position);
183        return RecipientEntry.constructTopLevelEntry(
184                c.getString(Queries.Query.NAME),
185                c.getInt(Queries.Query.DISPLAY_NAME_SOURCE),
186                c.getString(Queries.Query.DESTINATION),
187                c.getInt(Queries.Query.DESTINATION_TYPE),
188                c.getString(Queries.Query.DESTINATION_LABEL),
189                c.getLong(Queries.Query.CONTACT_ID),
190                c.getLong(Queries.Query.DATA_ID),
191                c.getString(Queries.Query.PHOTO_THUMBNAIL_URI));
192    }
193
194    @Override
195    public View getView(int position, View convertView, ViewGroup parent) {
196        Cursor cursor = getCursor();
197        cursor.moveToPosition(position);
198        if (convertView == null) {
199            convertView = newView();
200        }
201        if (cursor.getLong(Queries.Query.DATA_ID) == mCurrentId) {
202            mCheckedItemPosition = position;
203            if (mCheckedItemChangedListener != null) {
204                mCheckedItemChangedListener.onCheckedItemChanged(mCheckedItemPosition);
205            }
206        }
207        bindView(convertView, convertView.getContext(), cursor);
208        return convertView;
209    }
210
211    // TODO: this is VERY similar to the BaseRecipientAdapter. Can we combine
212    // somehow?
213    @Override
214    public void bindView(View view, Context context, Cursor cursor) {
215        int position = cursor.getPosition();
216
217        TextView display = (TextView) view.findViewById(android.R.id.title);
218        ImageView imageView = (ImageView) view.findViewById(android.R.id.icon);
219        RecipientEntry entry = getRecipientEntry(position);
220        if (position == 0) {
221            display.setText(cursor.getString(Queries.Query.NAME));
222            display.setVisibility(View.VISIBLE);
223            // TODO: see if this needs to be done outside the main thread
224            // as it may be too slow to get immediately.
225            imageView.setImageURI(entry.getPhotoThumbnailUri());
226            imageView.setVisibility(View.VISIBLE);
227        } else {
228            display.setVisibility(View.GONE);
229            imageView.setVisibility(View.GONE);
230        }
231        TextView destination = (TextView) view.findViewById(android.R.id.text1);
232        destination.setText(cursor.getString(Queries.Query.DESTINATION));
233
234        TextView destinationType = (TextView) view.findViewById(android.R.id.text2);
235        if (destinationType != null) {
236            destinationType.setText(mQuery.getTypeLabel(context.getResources(),
237                    cursor.getInt(Queries.Query.DESTINATION_TYPE),
238                    cursor.getString(Queries.Query.DESTINATION_LABEL)).toString().toUpperCase());
239        }
240    }
241
242    @Override
243    public View newView(Context context, Cursor cursor, ViewGroup parent) {
244        return newView();
245    }
246
247    private View newView() {
248        return mLayoutInflater.inflate(R.layout.chips_recipient_dropdown_item, null);
249    }
250
251    /*package*/ static interface OnCheckedItemChangedListener {
252        public void onCheckedItemChanged(int position);
253    }
254}
255