RecipientsAdapter.java revision f8d043174b15c48438c441635f29824bb525951b
1/* 2 * Copyright (C) 2008 Esmertec AG. 3 * Copyright (C) 2008 The Android Open Source Project 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.mms.ui; 19 20import com.android.internal.database.ArrayListCursor; 21import com.android.mms.R; 22import com.android.mms.data.Contact; 23 24import android.content.ContentResolver; 25import android.content.Context; 26import android.database.Cursor; 27import android.database.MergeCursor; 28import android.net.Uri; 29import android.provider.ContactsContract.Contacts; 30import android.provider.ContactsContract.Data; 31import android.provider.ContactsContract.RawContacts; 32import android.provider.ContactsContract.CommonDataKinds.Phone; 33import android.telephony.PhoneNumberUtils; 34import android.text.Annotation; 35import android.text.Spannable; 36import android.text.SpannableString; 37import android.text.TextUtils; 38import android.view.View; 39import android.widget.ResourceCursorAdapter; 40import android.widget.TextView; 41 42import java.util.ArrayList; 43 44/** 45 * This adapter is used to filter contacts on both name and number. 46 */ 47public class RecipientsAdapter extends ResourceCursorAdapter { 48 49 public static final int CONTACT_ID_INDEX = 1; 50 public static final int TYPE_INDEX = 2; 51 public static final int NUMBER_INDEX = 3; 52 public static final int LABEL_INDEX = 4; 53 public static final int NAME_INDEX = 5; 54 55 private static final String[] PROJECTION_PHONE = { 56 Data._ID, // 0 57 RawContacts.CONTACT_ID, // 1 58 Phone.TYPE, // 2 59 Phone.NUMBER, // 3 60 Phone.LABEL, // 4 61 Contacts.DISPLAY_NAME, // 5 62 }; 63 64 private static final String SORT_ORDER = Contacts.TIMES_CONTACTED + " DESC," 65 + Contacts.DISPLAY_NAME + "," + Phone.TYPE; 66 67 private final Context mContext; 68 private final ContentResolver mContentResolver; 69 70 public RecipientsAdapter(Context context) { 71 super(context, R.layout.recipient_filter_item, null); 72 mContext = context; 73 mContentResolver = context.getContentResolver(); 74 } 75 76 @Override 77 public final CharSequence convertToString(Cursor cursor) { 78 String name = cursor.getString(RecipientsAdapter.NAME_INDEX); 79 int type = cursor.getInt(RecipientsAdapter.TYPE_INDEX); 80 String number = cursor.getString(RecipientsAdapter.NUMBER_INDEX).trim(); 81 82 String label = cursor.getString(RecipientsAdapter.LABEL_INDEX); 83 CharSequence displayLabel = Phone.getDisplayLabel(mContext, type, label); 84 85 if (number.length() == 0) { 86 return number; 87 } 88 89 if (name == null) { 90 name = ""; 91 } 92 93 String nameAndNumber = Contact.formatNameAndNumber(name, number); 94 95 SpannableString out = new SpannableString(nameAndNumber); 96 int len = out.length(); 97 98 if (!TextUtils.isEmpty(name)) { 99 out.setSpan(new Annotation("name", name), 0, len, 100 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 101 } else { 102 out.setSpan(new Annotation("name", number), 0, len, 103 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 104 } 105 106 String person_id = cursor.getString(RecipientsAdapter.CONTACT_ID_INDEX); 107 out.setSpan(new Annotation("person_id", person_id), 0, len, 108 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 109 out.setSpan(new Annotation("label", displayLabel.toString()), 0, len, 110 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 111 out.setSpan(new Annotation("number", number), 0, len, 112 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 113 114 return out; 115 } 116 117 @Override 118 public final void bindView(View view, Context context, Cursor cursor) { 119 TextView name = (TextView) view.findViewById(R.id.name); 120 name.setText(cursor.getString(NAME_INDEX)); 121 122 TextView label = (TextView) view.findViewById(R.id.label); 123 int type = cursor.getInt(TYPE_INDEX); 124 label.setText(Phone.getDisplayLabel(mContext, type, cursor.getString(LABEL_INDEX))); 125 126 TextView number = (TextView) view.findViewById(R.id.number); 127 number.setText("(" + cursor.getString(NUMBER_INDEX) + ")"); 128 } 129 130 @Override 131 public Cursor runQueryOnBackgroundThread(CharSequence constraint) { 132 String phone = ""; 133 String cons = null; 134 135 if (constraint != null) { 136 cons = constraint.toString(); 137 138 if (usefulAsDigits(cons)) { 139 phone = PhoneNumberUtils.convertKeypadLettersToDigits(cons); 140 if (phone.equals(cons)) { 141 phone = ""; 142 } else { 143 phone = phone.trim(); 144 } 145 } 146 } 147 148 Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(cons)); 149 String selection = String.format("%s=%s OR %s=%s OR %s=%s", 150 Phone.TYPE, 151 Phone.TYPE_MOBILE, 152 Phone.TYPE, 153 Phone.TYPE_WORK_MOBILE, 154 Phone.TYPE, 155 Phone.TYPE_MMS); 156 Cursor phoneCursor = 157 mContentResolver.query(uri, 158 PROJECTION_PHONE, 159 selection, 160 null, 161 SORT_ORDER); 162 163 if (phone.length() > 0) { 164 ArrayList result = new ArrayList(); 165 result.add(Integer.valueOf(-1)); // ID 166 result.add(Long.valueOf(-1)); // PERSON_ID 167 result.add(Integer.valueOf(Phone.TYPE_CUSTOM)); // TYPE 168 result.add(phone); // NUMBER 169 170 /* 171 * The "\u00A0" keeps Phone.getDisplayLabel() from deciding 172 * to display the default label ("Home") next to the transformation 173 * of the letters into numbers. 174 */ 175 result.add("\u00A0"); // LABEL 176 result.add(cons); // NAME 177 178 ArrayList<ArrayList> wrap = new ArrayList<ArrayList>(); 179 wrap.add(result); 180 181 ArrayListCursor translated = new ArrayListCursor(PROJECTION_PHONE, wrap); 182 183 return new MergeCursor(new Cursor[] { translated, phoneCursor }); 184 } else { 185 return phoneCursor; 186 } 187 } 188 189 /** 190 * Returns true if all the characters are meaningful as digits 191 * in a phone number -- letters, digits, and a few punctuation marks. 192 */ 193 private boolean usefulAsDigits(CharSequence cons) { 194 int len = cons.length(); 195 196 for (int i = 0; i < len; i++) { 197 char c = cons.charAt(i); 198 199 if ((c >= '0') && (c <= '9')) { 200 continue; 201 } 202 if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+') 203 || (c == '#') || (c == '*')) { 204 continue; 205 } 206 if ((c >= 'A') && (c <= 'Z')) { 207 continue; 208 } 209 if ((c >= 'a') && (c <= 'z')) { 210 continue; 211 } 212 213 return false; 214 } 215 216 return true; 217 } 218} 219