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