• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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