• 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.internal.database.ArrayListCursor;
21 import com.android.mms.R;
22 import com.android.mms.data.Contact;
23 
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.database.Cursor;
27 import android.database.MergeCursor;
28 import android.net.Uri;
29 import android.provider.ContactsContract.Contacts;
30 import android.provider.ContactsContract.CommonDataKinds.Phone;
31 import android.telephony.PhoneNumberUtils;
32 import android.text.Annotation;
33 import android.text.Spannable;
34 import android.text.SpannableString;
35 import android.text.TextUtils;
36 import android.view.View;
37 import android.widget.ResourceCursorAdapter;
38 import android.widget.TextView;
39 
40 import java.util.ArrayList;
41 
42 /**
43  * This adapter is used to filter contacts on both name and number.
44  */
45 public class RecipientsAdapter extends ResourceCursorAdapter {
46 
47     public static final int CONTACT_ID_INDEX = 1;
48     public static final int TYPE_INDEX       = 2;
49     public static final int NUMBER_INDEX     = 3;
50     public static final int LABEL_INDEX      = 4;
51     public static final int NAME_INDEX       = 5;
52 
53     private static final String[] PROJECTION_PHONE = {
54         Phone._ID,                  // 0
55         Phone.CONTACT_ID,           // 1
56         Phone.TYPE,                 // 2
57         Phone.NUMBER,               // 3
58         Phone.LABEL,                // 4
59         Phone.DISPLAY_NAME,         // 5
60     };
61 
62     private static final String SORT_ORDER = Contacts.TIMES_CONTACTED + " DESC,"
63             + Contacts.DISPLAY_NAME + "," + Phone.TYPE;
64 
65     private final Context mContext;
66     private final ContentResolver mContentResolver;
67 
RecipientsAdapter(Context context)68     public RecipientsAdapter(Context context) {
69         super(context, R.layout.recipient_filter_item, null);
70         mContext = context;
71         mContentResolver = context.getContentResolver();
72     }
73 
74     @Override
convertToString(Cursor cursor)75     public final CharSequence convertToString(Cursor cursor) {
76         String name = cursor.getString(RecipientsAdapter.NAME_INDEX);
77         int type = cursor.getInt(RecipientsAdapter.TYPE_INDEX);
78         String number = cursor.getString(RecipientsAdapter.NUMBER_INDEX).trim();
79 
80         String label = cursor.getString(RecipientsAdapter.LABEL_INDEX);
81         CharSequence displayLabel = Phone.getDisplayLabel(mContext, type, label);
82 
83         if (number.length() == 0) {
84             return number;
85         }
86 
87         if (name == null) {
88             name = "";
89         } else {
90             // Names with commas are the bane of the recipient editor's existence.
91             // We've worked around them by using spans, but there are edge cases
92             // where the spans get deleted. Furthermore, having commas in names
93             // can be confusing to the user since commas are used as separators
94             // between recipients. The best solution is to simply remove commas
95             // from names.
96             name = name.replace(", ", " ")
97                        .replace(",", " ");  // Make sure we leave a space between parts of names.
98         }
99 
100         String nameAndNumber = Contact.formatNameAndNumber(name, number);
101 
102         SpannableString out = new SpannableString(nameAndNumber);
103         int len = out.length();
104 
105         if (!TextUtils.isEmpty(name)) {
106             out.setSpan(new Annotation("name", name), 0, len,
107                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
108         } else {
109             out.setSpan(new Annotation("name", number), 0, len,
110                         Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
111         }
112 
113         String person_id = cursor.getString(RecipientsAdapter.CONTACT_ID_INDEX);
114         out.setSpan(new Annotation("person_id", person_id), 0, len,
115                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
116         out.setSpan(new Annotation("label", displayLabel.toString()), 0, len,
117                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
118         out.setSpan(new Annotation("number", number), 0, len,
119                     Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
120 
121         return out;
122     }
123 
124     @Override
bindView(View view, Context context, Cursor cursor)125     public final void bindView(View view, Context context, Cursor cursor) {
126         TextView name = (TextView) view.findViewById(R.id.name);
127         name.setText(cursor.getString(NAME_INDEX));
128 
129         TextView label = (TextView) view.findViewById(R.id.label);
130         int type = cursor.getInt(TYPE_INDEX);
131         label.setText(Phone.getDisplayLabel(mContext, type, cursor.getString(LABEL_INDEX)));
132 
133         TextView number = (TextView) view.findViewById(R.id.number);
134         number.setText("(" + cursor.getString(NUMBER_INDEX) + ")");
135     }
136 
137     @Override
runQueryOnBackgroundThread(CharSequence constraint)138     public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
139         String phone = "";
140         String cons = null;
141 
142         if (constraint != null) {
143             cons = constraint.toString();
144 
145             if (usefulAsDigits(cons)) {
146                 phone = PhoneNumberUtils.convertKeypadLettersToDigits(cons);
147                 if (phone.equals(cons)) {
148                     phone = "";
149                 } else {
150                     phone = phone.trim();
151                 }
152             }
153         }
154 
155         Uri uri = Uri.withAppendedPath(Phone.CONTENT_FILTER_URI, Uri.encode(cons));
156         String selection = String.format("%s=%s OR %s=%s OR %s=%s",
157                 Phone.TYPE,
158                 Phone.TYPE_MOBILE,
159                 Phone.TYPE,
160                 Phone.TYPE_WORK_MOBILE,
161                 Phone.TYPE,
162                 Phone.TYPE_MMS);
163         Cursor phoneCursor =
164             mContentResolver.query(uri,
165                     PROJECTION_PHONE,
166                     selection,
167                     null,
168                     SORT_ORDER);
169 
170         if (phone.length() > 0) {
171             ArrayList result = new ArrayList();
172             result.add(Integer.valueOf(-1));                    // ID
173             result.add(Long.valueOf(-1));                       // CONTACT_ID
174             result.add(Integer.valueOf(Phone.TYPE_CUSTOM));     // TYPE
175             result.add(phone);                                  // NUMBER
176 
177             /*
178              * The "\u00A0" keeps Phone.getDisplayLabel() from deciding
179              * to display the default label ("Home") next to the transformation
180              * of the letters into numbers.
181              */
182             result.add("\u00A0");                               // LABEL
183             result.add(cons);                                   // NAME
184 
185             ArrayList<ArrayList> wrap = new ArrayList<ArrayList>();
186             wrap.add(result);
187 
188             ArrayListCursor translated = new ArrayListCursor(PROJECTION_PHONE, wrap);
189 
190             return new MergeCursor(new Cursor[] { translated, phoneCursor });
191         } else {
192             return phoneCursor;
193         }
194     }
195 
196     /**
197      * Returns true if all the characters are meaningful as digits
198      * in a phone number -- letters, digits, and a few punctuation marks.
199      */
usefulAsDigits(CharSequence cons)200     private boolean usefulAsDigits(CharSequence cons) {
201         int len = cons.length();
202 
203         for (int i = 0; i < len; i++) {
204             char c = cons.charAt(i);
205 
206             if ((c >= '0') && (c <= '9')) {
207                 continue;
208             }
209             if ((c == ' ') || (c == '-') || (c == '(') || (c == ')') || (c == '.') || (c == '+')
210                     || (c == '#') || (c == '*')) {
211                 continue;
212             }
213             if ((c >= 'A') && (c <= 'Z')) {
214                 continue;
215             }
216             if ((c >= 'a') && (c <= 'z')) {
217                 continue;
218             }
219 
220             return false;
221         }
222 
223         return true;
224     }
225 }
226