11d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickpackage com.android.mms.data;
21d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport java.io.IOException;
4b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport java.io.InputStream;
5513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagnerimport java.nio.CharBuffer;
61d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport java.util.ArrayList;
7b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport java.util.Arrays;
8513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagnerimport java.util.HashMap;
91d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport java.util.HashSet;
101d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport java.util.List;
111d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
121d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.content.ContentUris;
131d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.content.Context;
141d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.database.ContentObserver;
15b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.database.Cursor;
16b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.graphics.Bitmap;
17b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.graphics.BitmapFactory;
181c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagnerimport android.graphics.drawable.BitmapDrawable;
191c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagnerimport android.graphics.drawable.Drawable;
201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.net.Uri;
211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.os.Handler;
22a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Taoimport android.os.Parcelable;
232be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
24b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.provider.ContactsContract.Data;
252be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikovimport android.provider.ContactsContract.Presence;
26b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.provider.ContactsContract.CommonDataKinds.Email;
27b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.provider.ContactsContract.CommonDataKinds.Phone;
288d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylorimport android.provider.ContactsContract.Profile;
29f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms;
301d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.telephony.PhoneNumberUtils;
311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.text.TextUtils;
321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.util.Log;
331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
34fd644551e8506266aad2b76463b51b44154ed62fTom Taylorimport android.database.sqlite.SqliteWrapper;
3533a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrickimport com.android.mms.ui.MessageUtils;
3632a1dcecca6ed0881431555a4218d0282814326cWink Savilleimport com.android.mms.LogTag;
374759133e3c935e751815bf4f997d029caa64d889Bai Taoimport com.android.mms.MmsApp;
384f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylorimport com.android.mms.R;
391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
401d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickpublic class Contact {
41a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static final int CONTACT_METHOD_TYPE_UNKNOWN = 0;
42a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static final int CONTACT_METHOD_TYPE_PHONE = 1;
43a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static final int CONTACT_METHOD_TYPE_EMAIL = 2;
444f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    public static final int CONTACT_METHOD_TYPE_SELF = 3;       // the "Me" or profile contact
45a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static final String TEL_SCHEME = "tel";
46a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static final String CONTENT_SCHEME = "content";
47a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private static final int CONTACT_METHOD_ID_UNKNOWN = -1;
481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static final String TAG = "Contact";
4920b79e068c8aebff46f24fd3803cf7697df4eac4Ficus Kirkpatrick    private static final boolean V = false;
50b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private static ContactsCache sContactCache;
514f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private static final String SELF_ITEM_KEY = "Self_Item_Key";
521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
53d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//    private static final ContentObserver sContactsObserver = new ContentObserver(new Handler()) {
54d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        @Override
55d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        public void onChange(boolean selfUpdate) {
56d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
57d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//                log("contact changed, invalidate cache");
58d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            }
59d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            invalidateCache();
60d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        }
61d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//    };
62458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
63d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang    private static final ContentObserver sPresenceObserver = new ContentObserver(new Handler()) {
641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        @Override
651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        public void onChange(boolean selfUpdate) {
66d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
67d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang                log("presence changed, invalidate cache");
68d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang            }
691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            invalidateCache();
701d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
711d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    };
721d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
73ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner    private final static HashSet<UpdateListener> mListeners = new HashSet<UpdateListener>();
741d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
75a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private long mContactMethodId;   // Id in phone or email Uri returned by provider of current
76a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                                     // Contact, -1 is invalid. e.g. contact method id is 20 when
77a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                                     // current contact has phone content://.../phones/20.
78a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private int mContactMethodType;
791d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mNumber;
804759133e3c935e751815bf4f997d029caa64d889Bai Tao    private String mNumberE164;
811d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mName;
821d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mNameAndNumber;   // for display, e.g. Fred Flintstone <670-782-1123>
83431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    private boolean mNumberIsModified; // true if the number is modified
84431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
85431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    private long mRecipientId;       // used to find the Recipient cache entry
861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mLabel;
871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private long mPersonId;
881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private int mPresenceResId;      // TODO: make this a state instead of a res ID
891c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    private String mPresenceText;
901c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    private BitmapDrawable mAvatar;
91b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private byte [] mAvatarData;
92d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor    private boolean mIsStale;
93161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner    private boolean mQueryPending;
944f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private boolean mIsMe;          // true if this contact is me!
951b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    private boolean mSendToVoicemail;   // true if this contact should not put up notification
961d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
971d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public interface UpdateListener {
981d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        public void onUpdate(Contact updated);
991d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1001d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
101a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private Contact(String number, String name) {
102a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        init(number, name);
103a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
104b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    /*
105b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner     * Make a basic contact object with a phone number.
106b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner     */
1071d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private Contact(String number) {
108a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        init(number, "");
109a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
110a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
1114f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private Contact(boolean isMe) {
1124f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        init(SELF_ITEM_KEY, "");
1134f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        mIsMe = isMe;
1144f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1154f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
116a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private void init(String number, String name) {
117a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        mContactMethodId = CONTACT_METHOD_ID_UNKNOWN;
118a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        mName = name;
119431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        setNumber(number);
120431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = false;
1211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mLabel = "";
1221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mPersonId = 0;
1231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mPresenceResId = 0;
124d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        mIsStale = true;
1251b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        mSendToVoicemail = false;
1261d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
127b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    @Override
128b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    public String toString() {
129a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return String.format("{ number=%s, name=%s, nameAndNumber=%s, label=%s, person_id=%d, hash=%d method_id=%d }",
130feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mNumber != null ? mNumber : "null"),
131feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mName != null ? mName : "null"),
132feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mNameAndNumber != null ? mNameAndNumber : "null"),
133feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mLabel != null ? mLabel : "null"),
134a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                mPersonId, hashCode(),
135a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                mContactMethodId);
136b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    }
137b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
1381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static void logWithTrace(String msg, Object... format) {
1391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        Thread current = Thread.currentThread();
1401d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        StackTraceElement[] stack = current.getStackTrace();
1411d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1421d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        StringBuilder sb = new StringBuilder();
1431d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append("[");
1441d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(current.getId());
1451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append("] ");
1461d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(String.format(msg, format));
1471d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(" <- ");
1491d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        int stop = stack.length > 7 ? 7 : stack.length;
1501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (int i = 3; i < stop; i++) {
1511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            String methodName = stack[i].getMethodName();
1521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            sb.append(methodName);
1531d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            if ((i+1) != stop) {
1541d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                sb.append(" <- ");
1551d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
1561d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
1571d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        Log.d(TAG, sb.toString());
1591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static Contact get(String number, boolean canBlock) {
162b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        return sContactCache.get(number, canBlock);
1631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1654f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    public static Contact getMe(boolean canBlock) {
1664f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        return sContactCache.getMe(canBlock);
1674f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1684f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
169a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static List<Contact> getByPhoneUris(Parcelable[] uris) {
170a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return sContactCache.getContactInfoForPhoneUris(uris);
171a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
172a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
1731d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void invalidateCache() {
17432a1dcecca6ed0881431555a4218d0282814326cWink Saville        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
17532a1dcecca6ed0881431555a4218d0282814326cWink Saville            log("invalidateCache");
17632a1dcecca6ed0881431555a4218d0282814326cWink Saville        }
177d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor
178d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // While invalidating our local Cache doesn't remove the contacts, it will mark them
179d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // stale so the next time we're asked for a particular contact, we'll return that
180d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // stale contact and at the same time, fire off an asyncUpdateContact to update
181d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // that contact's info in the background. UI elements using the contact typically
182d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // call addListener() so they immediately get notified when the contact has been
183d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // updated with the latest info. They redraw themselves when we call the
184d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // listener's onUpdate().
185b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache.invalidate();
1861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1884f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    public boolean isMe() {
1894f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        return mIsMe;
1904f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1914f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
1921d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static String emptyIfNull(String s) {
1931d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return (s != null ? s : "");
1941d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1964759133e3c935e751815bf4f997d029caa64d889Bai Tao    /**
1974759133e3c935e751815bf4f997d029caa64d889Bai Tao     * Fomat the name and number.
1984759133e3c935e751815bf4f997d029caa64d889Bai Tao     *
1994759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param name
2004759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param number
2014759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param numberE164 the number's E.164 representation, is used to get the
2024759133e3c935e751815bf4f997d029caa64d889Bai Tao     *        country the number belongs to.
2034759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @return the formatted name and number
2044759133e3c935e751815bf4f997d029caa64d889Bai Tao     */
2053e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor    public static String formatNameAndNumber(String name, String number, String numberE164) {
2061d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        // Format like this: Mike Cleron <(650) 555-1234>
2071d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   Erick Tseng <(650) 555-1212>
2081d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   Tutankhamun <tutank1341@gmail.com>
2091d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   (408) 555-1289
2101d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        String formattedNumber = number;
2111d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (!Mms.isEmailAddress(number)) {
2123e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            formattedNumber = PhoneNumberUtils.formatNumber(number, numberE164,
2133e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor                    MmsApp.getApplication().getCurrentCountryIso());
2141d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2151d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (!TextUtils.isEmpty(name) && !name.equals(number)) {
2171d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return name + " <" + formattedNumber + ">";
2181d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        } else {
2191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return formattedNumber;
2201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
22360d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor    public synchronized void reload() {
22460d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor        mIsStale = true;
22560d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor        sContactCache.get(mNumber, false);
22660d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor    }
22760d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor
2281d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getNumber() {
2291d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mNumber;
2301d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
23225b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    public synchronized void setNumber(String number) {
2333e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        if (!Mms.isEmailAddress(number)) {
2343e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            mNumber = PhoneNumberUtils.formatNumber(number, mNumberE164,
2353e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor                    MmsApp.getApplication().getCurrentCountryIso());
2363e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        } else {
2373e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            mNumber = number;
2383e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        }
239dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang        notSynchronizedUpdateNameAndNumber();
240431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = true;
241431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
242431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
243431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public boolean isNumberModified() {
244431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        return mNumberIsModified;
245431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
246431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
2471b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    public boolean getSendToVoicemail() {
2481b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        return mSendToVoicemail;
2491b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    }
2501b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor
251431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public void setIsNumberModified(boolean flag) {
252431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = flag;
25325b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    }
25425b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang
2551d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getName() {
2561d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (TextUtils.isEmpty(mName)) {
2571d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return mNumber;
2581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        } else {
2591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return mName;
2601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2621d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getNameAndNumber() {
2641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mNameAndNumber;
2651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2661d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
267dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang    private void notSynchronizedUpdateNameAndNumber() {
2683e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        mNameAndNumber = formatNameAndNumber(mName, mNumber, mNumberE164);
26925b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    }
27025b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang
271431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public synchronized long getRecipientId() {
272431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        return mRecipientId;
273431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
274431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
275431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public synchronized void setRecipientId(long id) {
276431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mRecipientId = id;
277431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
278431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
2791d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getLabel() {
2801d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mLabel;
2811d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2821d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2831d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized Uri getUri() {
2842be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikov        return ContentUris.withAppendedId(Contacts.CONTENT_URI, mPersonId);
2851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized int getPresenceResId() {
2881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mPresenceResId;
2891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2901d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2911d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized boolean existsInDatabase() {
2921d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return (mPersonId > 0);
2931d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2941d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2953487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void addListener(UpdateListener l) {
2963487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
2973487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            mListeners.add(l);
2983487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        }
2991d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3001d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3013487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void removeListener(UpdateListener l) {
3023487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
3033487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            mListeners.remove(l);
3043487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        }
305d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang    }
306d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang
3073487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void dumpListeners() {
3083487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
3093487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            int i = 0;
3103487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            Log.i(TAG, "[Contact] dumpListeners; size=" + mListeners.size());
3113487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            for (UpdateListener listener : mListeners) {
3123487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner                Log.i(TAG, "["+ (i++) + "]" + listener);
3133487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            }
314d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        }
3151d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3171d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized boolean isEmail() {
3181d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return Mms.isEmailAddress(mNumber);
3191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3211c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    public String getPresenceText() {
3221c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner        return mPresenceText;
3231c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    }
3241c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner
325a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public int getContactMethodType() {
326a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return mContactMethodType;
327a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
328a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
329a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public long getContactMethodId() {
330a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return mContactMethodId;
331a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
332a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
333a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public synchronized Uri getPhoneUri() {
334a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        if (existsInDatabase()) {
335a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return ContentUris.withAppendedId(Phone.CONTENT_URI, mContactMethodId);
336a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        } else {
337a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            Uri.Builder ub = new Uri.Builder();
338a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            ub.scheme(TEL_SCHEME);
339a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            ub.encodedOpaquePart(mNumber);
340a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return ub.build();
341a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
342a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
343a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
344b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    public synchronized Drawable getAvatar(Context context, Drawable defaultValue) {
345b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        if (mAvatar == null) {
346b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (mAvatarData != null) {
347b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                Bitmap b = BitmapFactory.decodeByteArray(mAvatarData, 0, mAvatarData.length);
348b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mAvatar = new BitmapDrawable(context.getResources(), b);
349b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
350b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
3511c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner        return mAvatar != null ? mAvatar : defaultValue;
3521c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    }
3531c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner
3541d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void init(final Context context) {
355b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache = new ContactsCache(context);
356b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
3571d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        RecipientIdCache.init(context);
358d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang
359d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // it maybe too aggressive to listen for *any* contact changes, and rebuild MMS contact
360d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // cache each time that occurs. Unless we can get targeted updates for the contacts we
361d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // care about(which probably won't happen for a long time), we probably should just
362d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // invalidate cache peoridically, or surgically.
363d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        /*
3641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        context.getContentResolver().registerContentObserver(
3652be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikov                Contacts.CONTENT_URI, true, sContactsObserver);
366d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        */
3671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void dump() {
370b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache.dump();
371b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    }
372b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
373b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private static class ContactsCache {
374b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private final TaskStack mTaskQueue = new TaskStack();
375b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String SEPARATOR = ";";
376b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
3774759133e3c935e751815bf4f997d029caa64d889Bai Tao        /**
3784759133e3c935e751815bf4f997d029caa64d889Bai Tao         * For a specified phone number, 2 rows were inserted into phone_lookup
3794759133e3c935e751815bf4f997d029caa64d889Bai Tao         * table. One is the phone number's E164 representation, and another is
3804759133e3c935e751815bf4f997d029caa64d889Bai Tao         * one's normalized format. If the phone number's normalized format in
3814759133e3c935e751815bf4f997d029caa64d889Bai Tao         * the lookup table is the suffix of the given number's one, it is
3824759133e3c935e751815bf4f997d029caa64d889Bai Tao         * treated as matched CallerId. E164 format number must fully equal.
3834759133e3c935e751815bf4f997d029caa64d889Bai Tao         *
3844759133e3c935e751815bf4f997d029caa64d889Bai Tao         * For example: Both 650-123-4567 and +1 (650) 123-4567 will match the
3854759133e3c935e751815bf4f997d029caa64d889Bai Tao         * normalized number 6501234567 in the phone lookup.
3864759133e3c935e751815bf4f997d029caa64d889Bai Tao         *
3874759133e3c935e751815bf4f997d029caa64d889Bai Tao         *  The min_match is used to narrow down the candidates for the final
3884759133e3c935e751815bf4f997d029caa64d889Bai Tao         * comparison.
3894759133e3c935e751815bf4f997d029caa64d889Bai Tao         */
390b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // query params for caller id lookup
3914759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final String CALLER_ID_SELECTION = " Data._ID IN "
3924759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (SELECT DISTINCT lookup.data_id "
3934759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " FROM "
3944759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " (SELECT data_id, normalized_number, length(normalized_number) as len "
3954759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " FROM phone_lookup "
3964759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " WHERE min_match = ?) AS lookup "
3974759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " WHERE lookup.normalized_number = ? OR"
3984759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " (lookup.len <= ? AND "
3994759133e3c935e751815bf4f997d029caa64d889Bai Tao                        + " substr(?, ? - lookup.len + 1) = lookup.normalized_number))";
4004759133e3c935e751815bf4f997d029caa64d889Bai Tao
4014759133e3c935e751815bf4f997d029caa64d889Bai Tao        // query params for caller id lookup without E164 number as param
4024759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final String CALLER_ID_SELECTION_WITHOUT_E164 =  " Data._ID IN "
4034759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " (SELECT DISTINCT lookup.data_id "
4044759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " FROM "
4054759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (SELECT data_id, normalized_number, length(normalized_number) as len "
4064759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " FROM phone_lookup "
4074759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " WHERE min_match = ?) AS lookup "
4084759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " WHERE "
4094759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (lookup.len <= ? AND "
4104759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " substr(?, ? - lookup.len + 1) = lookup.normalized_number))";
411b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
412b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // Utilizing private API
413b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final Uri PHONES_WITH_PRESENCE_URI = Data.CONTENT_URI;
414b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
415b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String[] CALLER_ID_PROJECTION = new String[] {
416a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone._ID,                      // 0
417a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.NUMBER,                   // 1
418a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.LABEL,                    // 2
419a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.DISPLAY_NAME,             // 3
420a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_ID,               // 4
421a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_PRESENCE,         // 5
422a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_STATUS,           // 6
4231b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Phone.NORMALIZED_NUMBER,        // 7
4241b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Contacts.SEND_TO_VOICEMAIL      // 8
425b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        };
426b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
427a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_ID_COLUMN = 0;
428a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_NUMBER_COLUMN = 1;
429a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_LABEL_COLUMN = 2;
430a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_NAME_COLUMN = 3;
431a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_ID_COLUMN = 4;
432a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_PRESENCE_COLUMN = 5;
433a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_STATUS_COLUMN = 6;
4344759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final int PHONE_NORMALIZED_NUMBER = 7;
4351b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        private static final int SEND_TO_VOICEMAIL = 8;
436b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
4374f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final String[] SELF_PROJECTION = new String[] {
4384f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Phone._ID,                      // 0
4394f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Phone.DISPLAY_NAME,             // 1
4404f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        };
4414f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
4424f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final int SELF_ID_COLUMN = 0;
4434f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final int SELF_NAME_COLUMN = 1;
4444f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
445b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // query params for contact lookup by email
446b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final Uri EMAIL_WITH_PRESENCE_URI = Data.CONTENT_URI;
447b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
4480a3684e0f1242518b522e2883fe275343768bb27Dmitri Plotnikov        private static final String EMAIL_SELECTION = "UPPER(" + Email.DATA + ")=UPPER(?) AND "
4490a3684e0f1242518b522e2883fe275343768bb27Dmitri Plotnikov                + Data.MIMETYPE + "='" + Email.CONTENT_ITEM_TYPE + "'";
450b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
451b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String[] EMAIL_PROJECTION = new String[] {
452a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email._ID,                    // 0
453a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.DISPLAY_NAME,           // 1
454a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.CONTACT_PRESENCE,       // 2
455a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.CONTACT_ID,             // 3
456a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.DISPLAY_NAME,           // 4
4571b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Contacts.SEND_TO_VOICEMAIL    // 5
458b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        };
459a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_ID_COLUMN = 0;
460a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_NAME_COLUMN = 1;
461a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_STATUS_COLUMN = 2;
462a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_CONTACT_ID_COLUMN = 3;
463a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_CONTACT_NAME_COLUMN = 4;
4641b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        private static final int EMAIL_SEND_TO_VOICEMAIL_COLUMN = 5;
465b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
466b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private final Context mContext;
467458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
468513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        private final HashMap<String, ArrayList<Contact>> mContactsHash =
469513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            new HashMap<String, ArrayList<Contact>>();
470513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
471b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private ContactsCache(Context context) {
472b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            mContext = context;
473b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
474458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
475b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        void dump() {
476b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
477b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                Log.d(TAG, "**** Contact cache dump ****");
47840ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                for (String key : mContactsHash.keySet()) {
47940ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                    ArrayList<Contact> alc = mContactsHash.get(key);
480513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (Contact c : alc) {
48140ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                        Log.d(TAG, key + " ==> " + c.toString());
482513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    }
483b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
484b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
485b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
486161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner
487b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static class TaskStack {
488b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Thread mWorkerThread;
489b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            private final ArrayList<Runnable> mThingsToLoad;
490b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
491b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            public TaskStack() {
492b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mThingsToLoad = new ArrayList<Runnable>();
493b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mWorkerThread = new Thread(new Runnable() {
494b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    public void run() {
495b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        while (true) {
496b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            Runnable r = null;
497b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            synchronized (mThingsToLoad) {
498b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                if (mThingsToLoad.size() == 0) {
499b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    try {
500b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                        mThingsToLoad.wait();
501b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    } catch (InterruptedException ex) {
502b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                        // nothing to do
503b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    }
504b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                }
505b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                if (mThingsToLoad.size() > 0) {
506b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    r = mThingsToLoad.remove(0);
507161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                                }
508161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                            }
509b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            if (r != null) {
510b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                r.run();
511161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                            }
512161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                        }
513b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
514b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                });
515b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mWorkerThread.start();
516b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
517b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
518b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            public void push(Runnable r) {
519b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                synchronized (mThingsToLoad) {
520b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    mThingsToLoad.add(r);
521b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    mThingsToLoad.notify();
522b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
523b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
524b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
525b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
526b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        public void pushTask(Runnable r) {
527b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            mTaskQueue.push(r);
528b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
529b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
5304f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        public Contact getMe(boolean canBlock) {
5314f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return get(SELF_ITEM_KEY, true, canBlock);
5324f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
5334f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
534b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        public Contact get(String number, boolean canBlock) {
5354f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return get(number, false, canBlock);
5364f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
5374f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
5384f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact get(String number, boolean isMe, boolean canBlock) {
5394f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if (V) logWithTrace("get(%s, %s, %s)", number, isMe, canBlock);
540b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
541b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (TextUtils.isEmpty(number)) {
542b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                number = "";        // In some places (such as Korea), it's possible to receive
543b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // a message without the sender's address. In this case,
544b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // all such anonymous messages will get added to the same
545b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // thread.
546b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
547b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
548b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // Always return a Contact object, if if we don't have an actual contact
549b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // in the contacts db.
5504f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact contact = internalGet(number, isMe);
551b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Runnable r = null;
552dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
553b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (contact) {
554b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // If there's a query pending and we're willing to block then
555b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // wait here until the query completes.
556b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                while (canBlock && contact.mQueryPending) {
557b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    try {
558b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        contact.wait();
559b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    } catch (InterruptedException ex) {
560b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        // try again by virtue of the loop unless mQueryPending is false
561b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
562b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
563b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
564b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // If we're stale and we haven't already kicked off a query then kick
565b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // it off here.
566b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (contact.mIsStale && !contact.mQueryPending) {
567b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    contact.mIsStale = false;
568b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
569b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
570dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        log("async update for " + contact.toString() + " canBlock: " + canBlock +
571b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                " isStale: " + contact.mIsStale);
572b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
573b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
574b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    final Contact c = contact;
575b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    r = new Runnable() {
576b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        public void run() {
577b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            updateContact(c);
578161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                        }
579b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    };
580b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
581b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // set this to true while we have the lock on contact since we will
582b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // either run the query directly (canBlock case) or push the query
583b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // onto the queue.  In either case the mQueryPending will get set
584b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // to false via updateContact.
585b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    contact.mQueryPending = true;
586b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
587b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
588b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // do this outside of the synchronized so we don't hold up any
589b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // subsequent calls to "get" on other threads
590b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (r != null) {
591b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (canBlock) {
592b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    r.run();
593b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } else {
594b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    pushTask(r);
595b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
596b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
597b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return contact;
598b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
599b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
600a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        /**
601a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * Get CacheEntry list for given phone URIs. This method will do single one query to
602a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * get expected contacts from provider. Be sure passed in URIs are not null and contains
603a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * only valid URIs.
604a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         */
605a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        public List<Contact> getContactInfoForPhoneUris(Parcelable[] uris) {
606a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (uris.length == 0) {
607a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return null;
608a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
609a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            StringBuilder idSetBuilder = new StringBuilder();
610a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            boolean first = true;
611a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            for (Parcelable p : uris) {
612a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Uri uri = (Uri) p;
613a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                if ("content".equals(uri.getScheme())) {
614a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    if (first) {
615a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        first = false;
616a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        idSetBuilder.append(uri.getLastPathSegment());
617a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    } else {
618a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        idSetBuilder.append(',').append(uri.getLastPathSegment());
619a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    }
620a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
621a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
622a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            // Check whether there is content URI.
623a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (first) return null;
624a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            Cursor cursor = null;
625a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (idSetBuilder.length() > 0) {
626a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                final String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
627a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                cursor = mContext.getContentResolver().query(
628a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        PHONES_WITH_PRESENCE_URI, CALLER_ID_PROJECTION, whereClause, null, null);
629a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
630a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
631a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (cursor == null) {
632a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return null;
633a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
634a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
635a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            List<Contact> entries = new ArrayList<Contact>();
636a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
637a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            try {
638a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                while (cursor.moveToNext()) {
639a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    Contact entry = new Contact(cursor.getString(PHONE_NUMBER_COLUMN),
640a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                            cursor.getString(CONTACT_NAME_COLUMN));
641a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    fillPhoneTypeContact(entry, cursor);
642a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    ArrayList<Contact> value = new ArrayList<Contact>();
643a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    value.add(entry);
644a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    // Put the result in the cache.
645a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    mContactsHash.put(key(entry.mNumber, sStaticKeyBuffer), value);
646a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    entries.add(entry);
647a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
648a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            } finally {
649a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                cursor.close();
650a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
651a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return entries;
652a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
653a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
654b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private boolean contactChanged(Contact orig, Contact newContactData) {
655b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // The phone number should never change, so don't bother checking.
656b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // TODO: Maybe update it if it has gotten longer, i.e. 650-234-5678 -> +16502345678?
657b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
658a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            // Do the quick check first.
659a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (orig.mContactMethodType != newContactData.mContactMethodType) {
660b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
661b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
662b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
663a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (orig.mContactMethodId != newContactData.mContactMethodId) {
664b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
665b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
666b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
667b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (orig.mPersonId != newContactData.mPersonId) {
668b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (V) Log.d(TAG, "person id changed");
669b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
670b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
671b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
672b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (orig.mPresenceResId != newContactData.mPresenceResId) {
673b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (V) Log.d(TAG, "presence changed");
674b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
675b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
676b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
6771b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor            if (orig.mSendToVoicemail != newContactData.mSendToVoicemail) {
6781b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                return true;
6791b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor            }
6801b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor
681a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String oldName = emptyIfNull(orig.mName);
682a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String newName = emptyIfNull(newContactData.mName);
683a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (!oldName.equals(newName)) {
684a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                if (V) Log.d(TAG, String.format("name changed: %s -> %s", oldName, newName));
685a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return true;
686a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
687a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
688a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String oldLabel = emptyIfNull(orig.mLabel);
689a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String newLabel = emptyIfNull(newContactData.mLabel);
690a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (!oldLabel.equals(newLabel)) {
691a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                if (V) Log.d(TAG, String.format("label changed: %s -> %s", oldLabel, newLabel));
692a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return true;
693a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
694a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
695b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (!Arrays.equals(orig.mAvatarData, newContactData.mAvatarData)) {
696b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (V) Log.d(TAG, "avatar changed");
697b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
698b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
699b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
700b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return false;
701b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
702b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
703b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private void updateContact(final Contact c) {
704b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (c == null) {
705b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return;
706b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
707b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
7084f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact entry = getContactInfo(c);
709dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            synchronized (c) {
710b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (contactChanged(c, entry)) {
711b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
712b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        log("updateContact: contact changed for " + entry.mName);
713161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                    }
714b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
715b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mNumber = entry.mNumber;
716b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mLabel = entry.mLabel;
717b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPersonId = entry.mPersonId;
718b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPresenceResId = entry.mPresenceResId;
719b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPresenceText = entry.mPresenceText;
720b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mAvatarData = entry.mAvatarData;
721b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mAvatar = entry.mAvatar;
722a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    c.mContactMethodId = entry.mContactMethodId;
723a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    c.mContactMethodType = entry.mContactMethodType;
7244759133e3c935e751815bf4f997d029caa64d889Bai Tao                    c.mNumberE164 = entry.mNumberE164;
7254f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    c.mName = entry.mName;
7261b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                    c.mSendToVoicemail = entry.mSendToVoicemail;
727dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
728dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    c.notSynchronizedUpdateNameAndNumber();
729b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
730ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // We saw a bug where we were updating an empty contact. That would trigger
731ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // l.onUpdate() below, which would call ComposeMessageActivity.onUpdate,
732ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // which would call the adapter's notifyDataSetChanged, which would throw
733ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // away the message items and rebuild, eventually calling updateContact()
734ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // again -- all in a vicious and unending loop. Break the cycle and don't
735ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // notify if the number (the most important piece of information) is empty.
736ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    if (!TextUtils.isEmpty(c.mNumber)) {
737ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // clone the list of listeners in case the onUpdate call turns around and
738ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // modifies the list of listeners
739ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // access to mListeners is synchronized on ContactsCache
740ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        HashSet<UpdateListener> iterator;
741ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        synchronized (mListeners) {
742ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                            iterator = (HashSet<UpdateListener>)Contact.mListeners.clone();
743ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        }
744ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        for (UpdateListener l : iterator) {
745ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                            if (V) Log.d(TAG, "updating " + l);
746ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                            l.onUpdate(c);
747ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        }
748b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
749b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
750b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                synchronized (c) {
751b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mQueryPending = false;
752b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.notifyAll();
753161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                }
754b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
755161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner        }
756161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner
757b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
758b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Returns the caller info in Contact.
759b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
7604f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact getContactInfo(Contact c) {
7614f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if (c.mIsMe) {
7624f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForSelf();
7634f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            } else if (Mms.isEmailAddress(c.mNumber) || isAlphaNumber(c.mNumber)) {
7644f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForEmailAddress(c.mNumber);
765b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } else {
7664f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForPhoneNumber(c.mNumber);
767b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
76833a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick        }
76933a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick
7706ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Some received sms's have addresses such as "OakfieldCPS" or "T-Mobile". This
7716ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // function will attempt to identify these and return true. If the number contains
7726ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // 3 or more digits, such as "jello123", this function will return false.
7736ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Some countries have 3 digits shortcodes and we have to identify them as numbers.
7746ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    http://en.wikipedia.org/wiki/Short_code
7756ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Examples of input/output for this function:
7766ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Jello123" -> false  [3 digits, it is considered to be the phone number "123"]
7776ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "T-Mobile" -> true   [it is considered to be the address "T-Mobile"]
7786ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Mobile1"  -> true   [1 digit, it is considered to be the address "Mobile1"]
7796ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Dogs77"   -> true   [2 digits, it is considered to be the address "Dogs77"]
7806ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "****1"    -> true   [1 digits, it is considered to be the address "****1"]
7816ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "#4#5#6#"  -> true   [it is considered to be the address "#4#5#6#"]
7826ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "AB12"     -> true   [2 digits, it is considered to be the address "AB12"]
7836ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "12"       -> true   [2 digits, it is considered to be the address "12"]
7846ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        private boolean isAlphaNumber(String number) {
7856ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // TODO: PhoneNumberUtils.isWellFormedSmsAddress() only check if the number is a valid
7866ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // GSM SMS address. If the address contains a dialable char, it considers it a well
7876ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // formed SMS addr. CDMA doesn't work that way and has a different parser for SMS
7886ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // address (see CdmaSmsAddress.parse(String address)). We should definitely fix this!!!
7896ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            if (!PhoneNumberUtils.isWellFormedSmsAddress(number)) {
7906ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                // The example "T-Mobile" will exit here because there are no numbers.
7916ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                return true;        // we're not an sms address, consider it an alpha number
7926ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            }
793d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor            if (MessageUtils.isAlias(number)) {
794d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor                return true;
795d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor            }
7966ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            number = PhoneNumberUtils.extractNetworkPortion(number);
7976ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            if (TextUtils.isEmpty(number)) {
7986ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                return true;    // there are no digits whatsoever in the number
7996ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            }
8006ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // At this point, anything like "Mobile1" or "Dogs77" will be stripped down to
8016ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // "1" and "77". "#4#5#6#" remains as "#4#5#6#" at this point.
8026ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            return number.length() < 3;
8036ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        }
8046ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor
805b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
806b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Queries the caller id info with the phone number.
807b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * @return a Contact containing the caller id info corresponding to the number.
808b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
809b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private Contact getContactInfoForPhoneNumber(String number) {
810b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            number = PhoneNumberUtils.stripSeparators(number);
811b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Contact entry = new Contact(number);
812a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            entry.mContactMethodType = CONTACT_METHOD_TYPE_PHONE;
813b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
814b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            //if (LOCAL_DEBUG) log("queryContactInfoByNumber: number=" + number);
815b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8164759133e3c935e751815bf4f997d029caa64d889Bai Tao            String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
8174759133e3c935e751815bf4f997d029caa64d889Bai Tao            String minMatch = PhoneNumberUtils.toCallerIDMinMatch(normalizedNumber);
8184759133e3c935e751815bf4f997d029caa64d889Bai Tao            if (!TextUtils.isEmpty(normalizedNumber) && !TextUtils.isEmpty(minMatch)) {
8194759133e3c935e751815bf4f997d029caa64d889Bai Tao                String numberLen = String.valueOf(normalizedNumber.length());
8204759133e3c935e751815bf4f997d029caa64d889Bai Tao                String numberE164 = PhoneNumberUtils.formatNumberToE164(
8214759133e3c935e751815bf4f997d029caa64d889Bai Tao                        number, MmsApp.getApplication().getCurrentCountryIso());
8224759133e3c935e751815bf4f997d029caa64d889Bai Tao                String selection;
8234759133e3c935e751815bf4f997d029caa64d889Bai Tao                String[] args;
8244759133e3c935e751815bf4f997d029caa64d889Bai Tao                if (TextUtils.isEmpty(numberE164)) {
8254759133e3c935e751815bf4f997d029caa64d889Bai Tao                    selection = CALLER_ID_SELECTION_WITHOUT_E164;
8264759133e3c935e751815bf4f997d029caa64d889Bai Tao                    args = new String[] {minMatch, numberLen, normalizedNumber, numberLen};
8274759133e3c935e751815bf4f997d029caa64d889Bai Tao                } else {
8284759133e3c935e751815bf4f997d029caa64d889Bai Tao                    selection = CALLER_ID_SELECTION;
8294759133e3c935e751815bf4f997d029caa64d889Bai Tao                    args = new String[] {
8304759133e3c935e751815bf4f997d029caa64d889Bai Tao                            minMatch, numberE164, numberLen, normalizedNumber, numberLen};
8314759133e3c935e751815bf4f997d029caa64d889Bai Tao                }
832b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8334759133e3c935e751815bf4f997d029caa64d889Bai Tao                Cursor cursor = mContext.getContentResolver().query(
8344759133e3c935e751815bf4f997d029caa64d889Bai Tao                        PHONES_WITH_PRESENCE_URI, CALLER_ID_PROJECTION, selection, args, null);
8354759133e3c935e751815bf4f997d029caa64d889Bai Tao                if (cursor == null) {
8364759133e3c935e751815bf4f997d029caa64d889Bai Tao                    Log.w(TAG, "queryContactInfoByNumber(" + number + ") returned NULL cursor!"
8374759133e3c935e751815bf4f997d029caa64d889Bai Tao                            + " contact uri used " + PHONES_WITH_PRESENCE_URI);
8384759133e3c935e751815bf4f997d029caa64d889Bai Tao                    return entry;
8394759133e3c935e751815bf4f997d029caa64d889Bai Tao                }
840b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8414759133e3c935e751815bf4f997d029caa64d889Bai Tao                try {
8424759133e3c935e751815bf4f997d029caa64d889Bai Tao                    if (cursor.moveToFirst()) {
8434759133e3c935e751815bf4f997d029caa64d889Bai Tao                        fillPhoneTypeContact(entry, cursor);
8444759133e3c935e751815bf4f997d029caa64d889Bai Tao                    }
8454759133e3c935e751815bf4f997d029caa64d889Bai Tao                } finally {
8464759133e3c935e751815bf4f997d029caa64d889Bai Tao                    cursor.close();
847b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
848b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
849b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return entry;
85033a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick        }
85133a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick
8524f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        /**
8534f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor         * @return a Contact containing the info for the profile.
8544f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor         */
8554f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact getContactInfoForSelf() {
8564f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact entry = new Contact(true);
8574f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            entry.mContactMethodType = CONTACT_METHOD_TYPE_SELF;
8584f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
8594f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            //if (LOCAL_DEBUG) log("getContactInfoForSelf: number=" + number);
8604f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Cursor cursor = mContext.getContentResolver().query(
8614f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    Profile.CONTENT_URI, SELF_PROJECTION, null, null, null);
8624f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if (cursor == null) {
8634f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Log.w(TAG, "getContactInfoForSelf() returned NULL cursor!"
8644f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        + " contact uri used " + Profile.CONTENT_URI);
8654f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return entry;
8664f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
8674f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
8684f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            try {
8694f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                if (cursor.moveToFirst()) {
8704f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    fillSelfContact(entry, cursor);
8714f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
8724f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            } finally {
8734f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                cursor.close();
8744f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
8754f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return entry;
8764f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
8774f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
878a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private void fillPhoneTypeContact(final Contact contact, final Cursor cursor) {
879a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            synchronized (contact) {
880a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mContactMethodType = CONTACT_METHOD_TYPE_PHONE;
881a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mContactMethodId = cursor.getLong(PHONE_ID_COLUMN);
882a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mLabel = cursor.getString(PHONE_LABEL_COLUMN);
883a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mName = cursor.getString(CONTACT_NAME_COLUMN);
884a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPersonId = cursor.getLong(CONTACT_ID_COLUMN);
885a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPresenceResId = getPresenceIconResourceId(
886a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        cursor.getInt(CONTACT_PRESENCE_COLUMN));
887a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPresenceText = cursor.getString(CONTACT_STATUS_COLUMN);
8884759133e3c935e751815bf4f997d029caa64d889Bai Tao                contact.mNumberE164 = cursor.getString(PHONE_NORMALIZED_NUMBER);
8891b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                contact.mSendToVoicemail = cursor.getInt(SEND_TO_VOICEMAIL) == 1;
890a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                if (V) {
8914f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    log("fillPhoneTypeContact: name=" + contact.mName + ", number="
8921b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                            + contact.mNumber + ", presence=" + contact.mPresenceResId
8931b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                            + " SendToVoicemail: " + contact.mSendToVoicemail);
894a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
895a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
896a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            byte[] data = loadAvatarData(contact);
897a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
898a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            synchronized (contact) {
899a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mAvatarData = data;
900a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
901a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
9024f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
9034f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private void fillSelfContact(final Contact contact, final Cursor cursor) {
9044f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            synchronized (contact) {
9054f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                contact.mName = cursor.getString(SELF_NAME_COLUMN);
9064f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                if (TextUtils.isEmpty(contact.mName)) {
9074f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    contact.mName = mContext.getString(R.string.messagelist_sender_self);
9084f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
9094f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                if (V) {
9104f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    log("fillSelfContact: name=" + contact.mName + ", number="
9114f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                            + contact.mNumber);
9124f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
9134f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
9144f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            byte[] data = loadAvatarData(contact);
9154f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
9164f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            synchronized (contact) {
9174f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                contact.mAvatarData = data;
9184f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
9194f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
920b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /*
921b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Load the avatar data from the cursor into memory.  Don't decode the data
922b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * until someone calls for it (see getAvatar).  Hang onto the raw data so that
923b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * we can compare it when the data is reloaded.
924b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * TODO: consider comparing a checksum so that we don't have to hang onto
925b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * the raw bytes after the image is decoded.
926b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
927dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang        private byte[] loadAvatarData(Contact entry) {
928dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            byte [] data = null;
929dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
9304f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if ((!entry.mIsMe && entry.mPersonId == 0) || entry.mAvatar != null) {
931dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                return null;
932b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
933b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
934313d09ee558e6ac94ab28a205357885dc830cd0dTom Taylor            if (V) {
9358d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                log("loadAvatarData: name=" + entry.mName + ", number=" + entry.mNumber);
936313d09ee558e6ac94ab28a205357885dc830cd0dTom Taylor            }
937313d09ee558e6ac94ab28a205357885dc830cd0dTom Taylor
9388d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor            // If the contact is "me", then use my local profile photo. Otherwise, build a
9398d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor            // uri to get the avatar of the contact.
9404f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Uri contactUri = entry.mIsMe ?
9418d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                    Profile.CONTENT_URI :
9428d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                    ContentUris.withAppendedId(Contacts.CONTENT_URI, entry.mPersonId);
943b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
944dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
945b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        mContext.getContentResolver(),
946b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        contactUri);
947b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            try {
948b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (avatarDataStream != null) {
949dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    data = new byte[avatarDataStream.available()];
950b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    avatarDataStream.read(data, 0, data.length);
951b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
952b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } catch (IOException ex) {
953b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                //
954b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } finally {
955b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                try {
956b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (avatarDataStream != null) {
957b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        avatarDataStream.close();
958b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
959b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } catch (IOException e) {
960b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
961b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
962dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
963dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            return data;
9641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
9651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
966b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private int getPresenceIconResourceId(int presence) {
9676be18bedb5b87dbbcdb54f37d5a0945bd0f71377Tom Taylor            // TODO: must fix for SDK
968b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (presence != Presence.OFFLINE) {
969b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return Presence.getPresenceIconResourceId(presence);
970b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
971b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
972b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return 0;
973b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
974b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
975b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
976b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Query the contact email table to get the name of an email address.
977b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
978b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private Contact getContactInfoForEmailAddress(String email) {
979b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Contact entry = new Contact(email);
980a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            entry.mContactMethodType = CONTACT_METHOD_TYPE_EMAIL;
981b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
982b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Cursor cursor = SqliteWrapper.query(mContext, mContext.getContentResolver(),
983b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_WITH_PRESENCE_URI,
984b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_PROJECTION,
985b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_SELECTION,
986dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    new String[] { email },
987b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    null);
988b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
989b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (cursor != null) {
990b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                try {
991b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    while (cursor.moveToNext()) {
992dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        boolean found = false;
993a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        entry.mContactMethodId = cursor.getLong(EMAIL_ID_COLUMN);
994a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        entry.mPresenceResId = getPresenceIconResourceId(
995a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                                cursor.getInt(EMAIL_STATUS_COLUMN));
996a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        entry.mPersonId = cursor.getLong(EMAIL_CONTACT_ID_COLUMN);
9971b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                        entry.mSendToVoicemail = cursor.getInt(EMAIL_SEND_TO_VOICEMAIL_COLUMN) == 1;
998b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
999dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        synchronized (entry) {
1000dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            entry.mPresenceResId = getPresenceIconResourceId(
1001dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                    cursor.getInt(EMAIL_STATUS_COLUMN));
1002dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            entry.mPersonId = cursor.getLong(EMAIL_ID_COLUMN);
1003dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1004dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            String name = cursor.getString(EMAIL_NAME_COLUMN);
1005dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            if (TextUtils.isEmpty(name)) {
1006dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                name = cursor.getString(EMAIL_CONTACT_NAME_COLUMN);
1007dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            }
1008dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            if (!TextUtils.isEmpty(name)) {
1009dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                entry.mName = name;
1010dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                if (V) {
1011dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                    log("getContactInfoForEmailAddress: name=" + entry.mName +
1012dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                            ", email=" + email + ", presence=" +
1013dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                            entry.mPresenceResId);
1014dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                }
1015dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                found = true;
1016dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            }
1017b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        }
1018dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1019dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        if (found) {
1020dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            byte[] data = loadAvatarData(entry);
1021dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            synchronized (entry) {
1022dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                entry.mAvatarData = data;
1023b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            }
1024dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1025b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            break;
1026b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        }
1027b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
1028b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } finally {
1029b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    cursor.close();
10301d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
10311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
1032b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return entry;
10331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
10341d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1035513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // Invert and truncate to five characters the phoneNumber so that we
1036513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // can use it as the key in a hashtable.  We keep a mapping of this
1037513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // key to a list of all contacts which have the same key.
103840ab55a4337738075adbda3642ce0415f3810c4cMark Wagner        private String key(String phoneNumber, CharBuffer keyBuffer) {
1039513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.clear();
1040513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.mark();
1041513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
1042513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            int position = phoneNumber.length();
1043513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            int resultCount = 0;
1044513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            while (--position >= 0) {
1045513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                char c = phoneNumber.charAt(position);
1046513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                if (Character.isDigit(c)) {
1047513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    keyBuffer.put(c);
1048513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    if (++resultCount == STATIC_KEY_BUFFER_MAXIMUM_LENGTH) {
1049513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        break;
10501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    }
10511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
1052513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            }
1053513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.reset();
1054513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            if (resultCount > 0) {
105540ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                return keyBuffer.toString();
1056513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            } else {
1057513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // there were no usable digits in the input phoneNumber
1058513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                return phoneNumber;
10591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
10601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
10611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1062513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // Reuse this so we don't have to allocate each time we go through this
1063513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // "get" function.
1064513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        static final int STATIC_KEY_BUFFER_MAXIMUM_LENGTH = 5;
1065513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        static CharBuffer sStaticKeyBuffer = CharBuffer.allocate(STATIC_KEY_BUFFER_MAXIMUM_LENGTH);
10661d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
10674f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact internalGet(String numberOrEmail, boolean isMe) {
1068b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
1069513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // See if we can find "number" in the hashtable.
1070513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // If so, just return the result.
10714f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                final boolean isNotRegularPhoneNumber = isMe || Mms.isEmailAddress(numberOrEmail) ||
1072dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        MessageUtils.isAlias(numberOrEmail);
1073dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                final String key = isNotRegularPhoneNumber ?
1074dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        numberOrEmail : key(numberOrEmail, sStaticKeyBuffer);
1075513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
1076513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                ArrayList<Contact> candidates = mContactsHash.get(key);
1077513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                if (candidates != null) {
1078513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    int length = candidates.size();
1079513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (int i = 0; i < length; i++) {
1080513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        Contact c= candidates.get(i);
1081513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        if (isNotRegularPhoneNumber) {
1082513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            if (numberOrEmail.equals(c.mNumber)) {
1083513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                                return c;
1084513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            }
1085513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        } else {
1086513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            if (PhoneNumberUtils.compare(numberOrEmail, c.mNumber)) {
1087513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                                return c;
1088513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            }
1089513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        }
10901d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    }
1091513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                } else {
1092513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    candidates = new ArrayList<Contact>();
1093513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    // call toString() since it may be the static CharBuffer
109440ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                    mContactsHash.put(key, candidates);
10951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
10964f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Contact c = isMe ?
10974f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        new Contact(true) :
10984f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        new Contact(numberOrEmail);
1099513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                candidates.add(c);
1100161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                return c;
11011d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
11021d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
11031d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1104b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        void invalidate() {
1105d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            // Don't remove the contacts. Just mark them stale so we'll update their
1106d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            // info, particularly their presence.
1107b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
1108513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                for (ArrayList<Contact> alc : mContactsHash.values()) {
1109513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (Contact c : alc) {
1110dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        synchronized (c) {
1111dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            c.mIsStale = true;
1112dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        }
1113513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    }
1114d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor                }
1115d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            }
1116d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        }
111732a1dcecca6ed0881431555a4218d0282814326cWink Saville    }
111832a1dcecca6ed0881431555a4218d0282814326cWink Saville
111932a1dcecca6ed0881431555a4218d0282814326cWink Saville    private static void log(String msg) {
112032a1dcecca6ed0881431555a4218d0282814326cWink Saville        Log.d(TAG, msg);
11211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
11221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick}
1123