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