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