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 18 package com.android.mms.ui; 19 20 import com.android.mms.R; 21 import com.android.mms.ui.RecipientList.Recipient; 22 import com.google.android.mms.util.SqliteWrapper; 23 24 import android.content.ContentResolver; 25 import android.content.Context; 26 import com.android.internal.database.ArrayListCursor; 27 import android.database.Cursor; 28 import android.database.DatabaseUtils; 29 import android.database.MergeCursor; 30 import android.provider.Contacts; 31 import android.provider.Contacts.ContactMethods; 32 import android.provider.Contacts.Phones; 33 import android.telephony.PhoneNumberUtils; 34 import android.text.Annotation; 35 import android.text.Spannable; 36 import android.text.SpannableString; 37 import android.text.TextUtils; 38 import android.view.View; 39 import android.widget.ResourceCursorAdapter; 40 import android.widget.TextView; 41 42 import java.util.ArrayList; 43 44 /** 45 * This adapter is used to filter contacts on both name and number. 46 */ 47 public class RecipientsAdapter extends ResourceCursorAdapter { 48 49 public static final int PERSON_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 Contacts.Phones._ID, // 0 57 Contacts.Phones.PERSON_ID, // 1 58 Contacts.Phones.TYPE, // 2 59 Contacts.Phones.NUMBER, // 3 60 Contacts.Phones.LABEL, // 4 61 Contacts.Phones.NAME, // 5 62 }; 63 64 private static final String SORT_ORDER = "name, type"; 65 66 private final Context mContext; 67 private final ContentResolver mContentResolver; 68 RecipientsAdapter(Context context)69 public RecipientsAdapter(Context context) { 70 super(context, R.layout.recipient_filter_item, null); 71 mContext = context; 72 mContentResolver = context.getContentResolver(); 73 } 74 75 @Override convertToString(Cursor cursor)76 public final CharSequence convertToString(Cursor cursor) { 77 String name = cursor.getString(RecipientsAdapter.NAME_INDEX); 78 int type = cursor.getInt(RecipientsAdapter.TYPE_INDEX); 79 String number = cursor.getString(RecipientsAdapter.NUMBER_INDEX).trim(); 80 81 String label = cursor.getString(RecipientsAdapter.LABEL_INDEX); 82 CharSequence displayLabel = Phones.getDisplayLabel(mContext, type, label); 83 84 if (number.length() == 0) { 85 return number; 86 } 87 88 if (name == null) { 89 name = ""; 90 } 91 92 String nameAndNumber = Recipient.buildNameAndNumber(name, number); 93 94 SpannableString out = new SpannableString(nameAndNumber); 95 int len = out.length(); 96 97 if (!TextUtils.isEmpty(name)) { 98 out.setSpan(new Annotation("name", name), 0, len, 99 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 100 } else { 101 out.setSpan(new Annotation("name", number), 0, len, 102 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 103 } 104 105 String person_id = cursor.getString(RecipientsAdapter.PERSON_ID_INDEX); 106 out.setSpan(new Annotation("person_id", person_id), 0, len, 107 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 108 out.setSpan(new Annotation("label", displayLabel.toString()), 0, len, 109 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 110 out.setSpan(new Annotation("number", number), 0, len, 111 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 112 113 return out; 114 } 115 116 @Override bindView(View view, Context context, Cursor cursor)117 public final void bindView(View view, Context context, Cursor cursor) { 118 TextView name = (TextView) view.findViewById(R.id.name); 119 name.setText(cursor.getString(NAME_INDEX)); 120 121 TextView label = (TextView) view.findViewById(R.id.label); 122 int type = cursor.getInt(TYPE_INDEX); 123 label.setText(Phones.getDisplayLabel(mContext, type, cursor.getString(LABEL_INDEX))); 124 125 TextView number = (TextView) view.findViewById(R.id.number); 126 number.setText("(" + cursor.getString(NUMBER_INDEX) + ")"); 127 } 128 129 @Override runQueryOnBackgroundThread(CharSequence constraint)130 public Cursor runQueryOnBackgroundThread(CharSequence constraint) { 131 String wherePhone = null; 132 String whereEmail = null; 133 String phone = ""; 134 String cons = null; 135 136 if (constraint != null) { 137 cons = constraint.toString(); 138 139 if (usefulAsDigits(cons)) { 140 phone = PhoneNumberUtils.convertKeypadLettersToDigits(cons); 141 if (phone.equals(cons)) { 142 phone = ""; 143 } else { 144 phone = phone.trim(); 145 } 146 } 147 148 String filter = DatabaseUtils.sqlEscapeString(cons + '%'); 149 String filterLastName = DatabaseUtils.sqlEscapeString("% " + cons + '%'); 150 151 StringBuilder s = new StringBuilder(); 152 s.append("((name LIKE "); 153 s.append(filter); 154 s.append(") OR (name LIKE "); 155 s.append(filterLastName); 156 s.append(") OR (REPLACE(REPLACE(REPLACE(REPLACE(number, ' ', ''), '(', ''), ')', ''), '-', '') LIKE "); 157 s.append(filter); 158 s.append(")) AND type = "); 159 s.append(Phones.TYPE_MOBILE); 160 wherePhone = s.toString(); 161 } 162 163 Cursor phoneCursor = SqliteWrapper.query(mContext, mContentResolver, 164 Phones.CONTENT_URI, PROJECTION_PHONE, wherePhone, null, SORT_ORDER); 165 166 if (phone.length() > 0) { 167 ArrayList result = new ArrayList(); 168 result.add(Integer.valueOf(-1)); // ID 169 result.add(Long.valueOf(-1)); // PERSON_ID 170 result.add(Integer.valueOf(Phones.TYPE_CUSTOM)); // TYPE 171 result.add(phone); // NUMBER 172 173 /* 174 * The "\u00A0" keeps Phones.getDisplayLabel() from deciding 175 * to display the default label ("Home") next to the transformation 176 * of the letters into numbers. 177 */ 178 result.add("\u00A0"); // LABEL 179 result.add(cons); // NAME 180 181 ArrayList<ArrayList> wrap = new ArrayList<ArrayList>(); 182 wrap.add(result); 183 184 ArrayListCursor translated = new ArrayListCursor(PROJECTION_PHONE, wrap); 185 186 return new MergeCursor(new Cursor[] { translated, phoneCursor }); 187 } else { 188 return phoneCursor; 189 } 190 } 191 192 /** 193 * Returns true if all the characters are meaningful as digits 194 * in a phone number -- letters, digits, and a few punctuation marks. 195 */ usefulAsDigits(CharSequence cons)196 private boolean usefulAsDigits(CharSequence cons) { 197 int len = cons.length(); 198 199 for (int i = 0; i < len; i++) { 200 char c = cons.charAt(i); 201 202 if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.')) { 203 continue; 204 } 205 if ((c >= 'A') && (c <= 'Z')) { 206 continue; 207 } 208 if ((c >= 'a') && (c <= 'z')) { 209 continue; 210 } 211 if ((c >= '0') && (c <= '9')) { 212 continue; 213 } 214 215 return false; 216 } 217 218 return true; 219 } 220 } 221