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;
16d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.database.sqlite.SqliteWrapper;
17b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.graphics.Bitmap;
18b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.graphics.BitmapFactory;
191c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagnerimport android.graphics.drawable.BitmapDrawable;
201c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagnerimport android.graphics.drawable.Drawable;
211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.net.Uri;
221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.os.Handler;
23a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Taoimport android.os.Parcelable;
24d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.provider.ContactsContract.CommonDataKinds.Email;
25d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport android.provider.ContactsContract.CommonDataKinds.Phone;
262be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikovimport android.provider.ContactsContract.Contacts;
27b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagnerimport android.provider.ContactsContract.Data;
282be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikovimport android.provider.ContactsContract.Presence;
298d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylorimport android.provider.ContactsContract.Profile;
30f7e8281a223af6228e6399055a6197a1edd9bc3aTom Taylorimport android.provider.Telephony.Mms;
311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.telephony.PhoneNumberUtils;
321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.text.TextUtils;
331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrickimport android.util.Log;
341d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3532a1dcecca6ed0881431555a4218d0282814326cWink Savilleimport com.android.mms.LogTag;
364759133e3c935e751815bf4f997d029caa64d889Bai Taoimport com.android.mms.MmsApp;
374f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylorimport com.android.mms.R;
38d64419030e1fec1e751695dab3bd7236e2fb0214Roger Chenimport com.android.mms.ui.MessageUtils;
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";
49b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private static ContactsCache sContactCache;
504f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private static final String SELF_ITEM_KEY = "Self_Item_Key";
511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
52d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//    private static final ContentObserver sContactsObserver = new ContentObserver(new Handler()) {
53d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        @Override
54d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        public void onChange(boolean selfUpdate) {
55d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
56d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//                log("contact changed, invalidate cache");
57d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            }
58d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//            invalidateCache();
59d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//        }
60d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor//    };
61458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
62d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang    private static final ContentObserver sPresenceObserver = new ContentObserver(new Handler()) {
631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        @Override
641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        public void onChange(boolean selfUpdate) {
65d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang            if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
66d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang                log("presence changed, invalidate cache");
67d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang            }
681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            invalidateCache();
691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
701d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    };
711d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
72ea5e915b6104ff0e30c313bcb5a96f838997e57bMark Wagner    private final static HashSet<UpdateListener> mListeners = new HashSet<UpdateListener>();
731d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
74a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private long mContactMethodId;   // Id in phone or email Uri returned by provider of current
75a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                                     // Contact, -1 is invalid. e.g. contact method id is 20 when
76a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                                     // current contact has phone content://.../phones/20.
77a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private int mContactMethodType;
781d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mNumber;
794759133e3c935e751815bf4f997d029caa64d889Bai Tao    private String mNumberE164;
801d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mName;
811d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mNameAndNumber;   // for display, e.g. Fred Flintstone <670-782-1123>
82431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    private boolean mNumberIsModified; // true if the number is modified
83431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
84431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    private long mRecipientId;       // used to find the Recipient cache entry
851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private String mLabel;
861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private long mPersonId;
871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private int mPresenceResId;      // TODO: make this a state instead of a res ID
881c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    private String mPresenceText;
891c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    private BitmapDrawable mAvatar;
90b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private byte [] mAvatarData;
91d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor    private boolean mIsStale;
92161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner    private boolean mQueryPending;
934f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private boolean mIsMe;          // true if this contact is me!
941b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    private boolean mSendToVoicemail;   // true if this contact should not put up notification
951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
961d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public interface UpdateListener {
971d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        public void onUpdate(Contact updated);
981d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
991d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
100a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private Contact(String number, String name) {
101a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        init(number, name);
102a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
103b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    /*
104b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner     * Make a basic contact object with a phone number.
105b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner     */
1061d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private Contact(String number) {
107a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        init(number, "");
108a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
109a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
1104f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    private Contact(boolean isMe) {
1114f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        init(SELF_ITEM_KEY, "");
1124f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        mIsMe = isMe;
1134f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1144f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
115a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    private void init(String number, String name) {
116a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        mContactMethodId = CONTACT_METHOD_ID_UNKNOWN;
117a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        mName = name;
118431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        setNumber(number);
119431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = false;
1201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mLabel = "";
1211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mPersonId = 0;
1221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        mPresenceResId = 0;
123d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        mIsStale = true;
1241b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        mSendToVoicemail = false;
1251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
126b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    @Override
127b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    public String toString() {
128a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return String.format("{ number=%s, name=%s, nameAndNumber=%s, label=%s, person_id=%d, hash=%d method_id=%d }",
129feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mNumber != null ? mNumber : "null"),
130feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mName != null ? mName : "null"),
131feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mNameAndNumber != null ? mNameAndNumber : "null"),
132feb1aab54eabc98a04349af52e19ff9fbcd7c169Tom Taylor                (mLabel != null ? mLabel : "null"),
133a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                mPersonId, hashCode(),
134a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                mContactMethodId);
135b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    }
136b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
137d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor    public static void logWithTrace(String tag, String msg, Object... format) {
1381d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        Thread current = Thread.currentThread();
1391d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        StackTraceElement[] stack = current.getStackTrace();
1401d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1411d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        StringBuilder sb = new StringBuilder();
1421d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append("[");
1431d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(current.getId());
1441d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append("] ");
1451d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(String.format(msg, format));
1461d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1471d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        sb.append(" <- ");
1481d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        int stop = stack.length > 7 ? 7 : stack.length;
1491d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        for (int i = 3; i < stop; i++) {
1501d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            String methodName = stack[i].getMethodName();
1511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            sb.append(methodName);
1521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            if ((i+1) != stop) {
1531d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                sb.append(" <- ");
1541d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
1551d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
1561d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
157d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor        Log.d(tag, sb.toString());
1581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static Contact get(String number, boolean canBlock) {
161b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        return sContactCache.get(number, canBlock);
1621d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1644f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    public static Contact getMe(boolean canBlock) {
1654f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        return sContactCache.getMe(canBlock);
1664f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1674f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
1681b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor    public void removeFromCache() {
1691b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor        sContactCache.remove(this);
1701b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor    }
1711b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor
172a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public static List<Contact> getByPhoneUris(Parcelable[] uris) {
173a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return sContactCache.getContactInfoForPhoneUris(uris);
174a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
175a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
1761d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void invalidateCache() {
17732a1dcecca6ed0881431555a4218d0282814326cWink Saville        if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
17832a1dcecca6ed0881431555a4218d0282814326cWink Saville            log("invalidateCache");
17932a1dcecca6ed0881431555a4218d0282814326cWink Saville        }
180d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor
181d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // While invalidating our local Cache doesn't remove the contacts, it will mark them
182d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // stale so the next time we're asked for a particular contact, we'll return that
183d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // stale contact and at the same time, fire off an asyncUpdateContact to update
184d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // that contact's info in the background. UI elements using the contact typically
185d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // call addListener() so they immediately get notified when the contact has been
186d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // updated with the latest info. They redraw themselves when we call the
187d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        // listener's onUpdate().
188b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache.invalidate();
1891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1901d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1914f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    public boolean isMe() {
1924f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        return mIsMe;
1934f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor    }
1944f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
1951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    private static String emptyIfNull(String s) {
1961d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return (s != null ? s : "");
1971d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
1981d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1994759133e3c935e751815bf4f997d029caa64d889Bai Tao    /**
2004759133e3c935e751815bf4f997d029caa64d889Bai Tao     * Fomat the name and number.
2014759133e3c935e751815bf4f997d029caa64d889Bai Tao     *
2024759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param name
2034759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param number
2044759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @param numberE164 the number's E.164 representation, is used to get the
2054759133e3c935e751815bf4f997d029caa64d889Bai Tao     *        country the number belongs to.
2064759133e3c935e751815bf4f997d029caa64d889Bai Tao     * @return the formatted name and number
2074759133e3c935e751815bf4f997d029caa64d889Bai Tao     */
2083e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor    public static String formatNameAndNumber(String name, String number, String numberE164) {
2091d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        // Format like this: Mike Cleron <(650) 555-1234>
2101d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   Erick Tseng <(650) 555-1212>
2111d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   Tutankhamun <tutank1341@gmail.com>
2121d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        //                   (408) 555-1289
2131d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        String formattedNumber = number;
2141d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (!Mms.isEmailAddress(number)) {
2153e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            formattedNumber = PhoneNumberUtils.formatNumber(number, numberE164,
2163e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor                    MmsApp.getApplication().getCurrentCountryIso());
2171d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2181d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (!TextUtils.isEmpty(name) && !name.equals(number)) {
2201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return name + " <" + formattedNumber + ">";
2211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        } else {
2221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return formattedNumber;
2231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2241d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2251d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
22660d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor    public synchronized void reload() {
22760d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor        mIsStale = true;
22860d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor        sContactCache.get(mNumber, false);
22960d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor    }
23060d7fa5f74463f22fe9a4cd81c0b2fcc95be7c09Tom Taylor
2311d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getNumber() {
2321d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mNumber;
2331d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2341d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
23525b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    public synchronized void setNumber(String number) {
2363e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        if (!Mms.isEmailAddress(number)) {
2373e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            mNumber = PhoneNumberUtils.formatNumber(number, mNumberE164,
2383e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor                    MmsApp.getApplication().getCurrentCountryIso());
2393e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        } else {
2403e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor            mNumber = number;
2413e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        }
242dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang        notSynchronizedUpdateNameAndNumber();
243431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = true;
244431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
245431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
246431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public boolean isNumberModified() {
247431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        return mNumberIsModified;
248431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
249431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
2501b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    public boolean getSendToVoicemail() {
2511b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        return mSendToVoicemail;
2521b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor    }
2531b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor
254431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public void setIsNumberModified(boolean flag) {
255431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mNumberIsModified = flag;
25625b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    }
25725b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang
2581d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getName() {
2591d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        if (TextUtils.isEmpty(mName)) {
2601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return mNumber;
2611d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        } else {
2621d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            return mName;
2631d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
2641d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2651d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2661d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getNameAndNumber() {
2671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mNameAndNumber;
2681d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2691d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
270dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang    private void notSynchronizedUpdateNameAndNumber() {
2713e54d384bed81d27dbe15a2a69344d5e64a1c018Tom Taylor        mNameAndNumber = formatNameAndNumber(mName, mNumber, mNumberE164);
27225b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang    }
27325b939e5a7ecb1e0879b684dc5bc55183cf468b4Wei Huang
274431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public synchronized long getRecipientId() {
275431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        return mRecipientId;
276431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
277431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
278431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    public synchronized void setRecipientId(long id) {
279431e11d429783c07eca6e789de0518d17b42fb74Wei Huang        mRecipientId = id;
280431e11d429783c07eca6e789de0518d17b42fb74Wei Huang    }
281431e11d429783c07eca6e789de0518d17b42fb74Wei Huang
2821d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized String getLabel() {
2831d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mLabel;
2841d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2851d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2861d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized Uri getUri() {
2872be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikov        return ContentUris.withAppendedId(Contacts.CONTENT_URI, mPersonId);
2881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2901d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized int getPresenceResId() {
2911d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return mPresenceResId;
2921d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2931d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2941d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized boolean existsInDatabase() {
2951d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return (mPersonId > 0);
2961d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
2971d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
2983487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void addListener(UpdateListener l) {
2993487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
3003487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            mListeners.add(l);
3013487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        }
3021d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3031d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3043487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void removeListener(UpdateListener l) {
3053487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
3063487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            mListeners.remove(l);
3073487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        }
308d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang    }
309d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang
3103487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner    public static void dumpListeners() {
3113487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner        synchronized (mListeners) {
3123487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            int i = 0;
3133487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            Log.i(TAG, "[Contact] dumpListeners; size=" + mListeners.size());
3143487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            for (UpdateListener listener : mListeners) {
3153487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner                Log.i(TAG, "["+ (i++) + "]" + listener);
3163487a66797bbf5339c59750c0d3d7fb703626730Mark Wagner            }
317d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        }
3181d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3191d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3201d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public synchronized boolean isEmail() {
3211d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        return Mms.isEmailAddress(mNumber);
3221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3241c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    public String getPresenceText() {
3251c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner        return mPresenceText;
3261c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    }
3271c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner
328a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public int getContactMethodType() {
329a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return mContactMethodType;
330a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
331a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
332a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public long getContactMethodId() {
333a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        return mContactMethodId;
334a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
335a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
336a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    public synchronized Uri getPhoneUri() {
337a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        if (existsInDatabase()) {
338a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return ContentUris.withAppendedId(Phone.CONTENT_URI, mContactMethodId);
339a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        } else {
340a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            Uri.Builder ub = new Uri.Builder();
341a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            ub.scheme(TEL_SCHEME);
342a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            ub.encodedOpaquePart(mNumber);
343a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return ub.build();
344a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
345a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao    }
346a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
347b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    public synchronized Drawable getAvatar(Context context, Drawable defaultValue) {
348b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        if (mAvatar == null) {
349b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (mAvatarData != null) {
350b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                Bitmap b = BitmapFactory.decodeByteArray(mAvatarData, 0, mAvatarData.length);
351b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mAvatar = new BitmapDrawable(context.getResources(), b);
352b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
353b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
3541c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner        return mAvatar != null ? mAvatar : defaultValue;
3551c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner    }
3561c59b33adcdc15a035fce8bd37cd02cd063b4274Mark Wagner
3571d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void init(final Context context) {
358b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache = new ContactsCache(context);
359b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
3601d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        RecipientIdCache.init(context);
361d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang
362d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // it maybe too aggressive to listen for *any* contact changes, and rebuild MMS contact
363d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // cache each time that occurs. Unless we can get targeted updates for the contacts we
364d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // care about(which probably won't happen for a long time), we probably should just
365d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        // invalidate cache peoridically, or surgically.
366d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        /*
3671d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        context.getContentResolver().registerContentObserver(
3682be1333ab5b265c0ff758354bac6a9eff97e1facDmitri Plotnikov                Contacts.CONTENT_URI, true, sContactsObserver);
369d9d7479a8c9e10b7b3f39137e28ed0f283e4a257Wei Huang        */
3701d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
3711d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
3721d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    public static void dump() {
373b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        sContactCache.dump();
374b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    }
375b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
376b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner    private static class ContactsCache {
377b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private final TaskStack mTaskQueue = new TaskStack();
378b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String SEPARATOR = ";";
379b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
3804759133e3c935e751815bf4f997d029caa64d889Bai Tao        /**
3814759133e3c935e751815bf4f997d029caa64d889Bai Tao         * For a specified phone number, 2 rows were inserted into phone_lookup
3824759133e3c935e751815bf4f997d029caa64d889Bai Tao         * table. One is the phone number's E164 representation, and another is
3834759133e3c935e751815bf4f997d029caa64d889Bai Tao         * one's normalized format. If the phone number's normalized format in
3844759133e3c935e751815bf4f997d029caa64d889Bai Tao         * the lookup table is the suffix of the given number's one, it is
3854759133e3c935e751815bf4f997d029caa64d889Bai Tao         * treated as matched CallerId. E164 format number must fully equal.
3864759133e3c935e751815bf4f997d029caa64d889Bai Tao         *
3874759133e3c935e751815bf4f997d029caa64d889Bai Tao         * For example: Both 650-123-4567 and +1 (650) 123-4567 will match the
3884759133e3c935e751815bf4f997d029caa64d889Bai Tao         * normalized number 6501234567 in the phone lookup.
3894759133e3c935e751815bf4f997d029caa64d889Bai Tao         *
3904759133e3c935e751815bf4f997d029caa64d889Bai Tao         *  The min_match is used to narrow down the candidates for the final
3914759133e3c935e751815bf4f997d029caa64d889Bai Tao         * comparison.
3924759133e3c935e751815bf4f997d029caa64d889Bai Tao         */
393b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // query params for caller id lookup
3944759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final String CALLER_ID_SELECTION = " Data._ID IN "
3954759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (SELECT DISTINCT lookup.data_id "
3964759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " FROM "
3974759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " (SELECT data_id, normalized_number, length(normalized_number) as len "
3984759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " FROM phone_lookup "
3994759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " WHERE min_match = ?) AS lookup "
4004759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " WHERE lookup.normalized_number = ? OR"
4014759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " (lookup.len <= ? AND "
4024759133e3c935e751815bf4f997d029caa64d889Bai Tao                        + " substr(?, ? - lookup.len + 1) = lookup.normalized_number))";
4034759133e3c935e751815bf4f997d029caa64d889Bai Tao
4044759133e3c935e751815bf4f997d029caa64d889Bai Tao        // query params for caller id lookup without E164 number as param
4054759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final String CALLER_ID_SELECTION_WITHOUT_E164 =  " Data._ID IN "
4064759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " (SELECT DISTINCT lookup.data_id "
4074759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " FROM "
4084759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (SELECT data_id, normalized_number, length(normalized_number) as len "
4094759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " FROM phone_lookup "
4104759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " WHERE min_match = ?) AS lookup "
4114759133e3c935e751815bf4f997d029caa64d889Bai Tao            + " WHERE "
4124759133e3c935e751815bf4f997d029caa64d889Bai Tao                + " (lookup.len <= ? AND "
4134759133e3c935e751815bf4f997d029caa64d889Bai Tao                    + " substr(?, ? - lookup.len + 1) = lookup.normalized_number))";
414b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
415b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // Utilizing private API
416b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final Uri PHONES_WITH_PRESENCE_URI = Data.CONTENT_URI;
417b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
418b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String[] CALLER_ID_PROJECTION = new String[] {
419a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone._ID,                      // 0
420a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.NUMBER,                   // 1
421a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.LABEL,                    // 2
422a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.DISPLAY_NAME,             // 3
423a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_ID,               // 4
424a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_PRESENCE,         // 5
425a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.CONTACT_STATUS,           // 6
4261b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Phone.NORMALIZED_NUMBER,        // 7
4271b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Contacts.SEND_TO_VOICEMAIL      // 8
428b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        };
429b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
430a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_ID_COLUMN = 0;
431a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_NUMBER_COLUMN = 1;
432a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int PHONE_LABEL_COLUMN = 2;
433a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_NAME_COLUMN = 3;
434a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_ID_COLUMN = 4;
435a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_PRESENCE_COLUMN = 5;
436a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int CONTACT_STATUS_COLUMN = 6;
4374759133e3c935e751815bf4f997d029caa64d889Bai Tao        private static final int PHONE_NORMALIZED_NUMBER = 7;
4381b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        private static final int SEND_TO_VOICEMAIL = 8;
439b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
4404f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final String[] SELF_PROJECTION = new String[] {
4414f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Phone._ID,                      // 0
4424f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Phone.DISPLAY_NAME,             // 1
4434f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        };
4444f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
4454f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final int SELF_ID_COLUMN = 0;
4464f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private static final int SELF_NAME_COLUMN = 1;
4474f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
448b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        // query params for contact lookup by email
449b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final Uri EMAIL_WITH_PRESENCE_URI = Data.CONTENT_URI;
450b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
4510a3684e0f1242518b522e2883fe275343768bb27Dmitri Plotnikov        private static final String EMAIL_SELECTION = "UPPER(" + Email.DATA + ")=UPPER(?) AND "
4520a3684e0f1242518b522e2883fe275343768bb27Dmitri Plotnikov                + Data.MIMETYPE + "='" + Email.CONTENT_ITEM_TYPE + "'";
453b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
454b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static final String[] EMAIL_PROJECTION = new String[] {
455a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email._ID,                    // 0
456a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.DISPLAY_NAME,           // 1
457a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.CONTACT_PRESENCE,       // 2
458a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Email.CONTACT_ID,             // 3
459a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Phone.DISPLAY_NAME,           // 4
4601b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                Contacts.SEND_TO_VOICEMAIL    // 5
461b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        };
462a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_ID_COLUMN = 0;
463a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_NAME_COLUMN = 1;
464a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_STATUS_COLUMN = 2;
465a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_CONTACT_ID_COLUMN = 3;
466a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private static final int EMAIL_CONTACT_NAME_COLUMN = 4;
4671b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor        private static final int EMAIL_SEND_TO_VOICEMAIL_COLUMN = 5;
468b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
469b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private final Context mContext;
470458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
471513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        private final HashMap<String, ArrayList<Contact>> mContactsHash =
472513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            new HashMap<String, ArrayList<Contact>>();
473513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
474b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private ContactsCache(Context context) {
475b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            mContext = context;
476b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
477458dea8d1858e418b6918f477d0139335b14ff45Tom Taylor
478b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        void dump() {
479b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
480b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                Log.d(TAG, "**** Contact cache dump ****");
48140ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                for (String key : mContactsHash.keySet()) {
48240ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                    ArrayList<Contact> alc = mContactsHash.get(key);
483513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (Contact c : alc) {
48440ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                        Log.d(TAG, key + " ==> " + c.toString());
485513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    }
486b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
487b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
488b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
489161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner
490b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private static class TaskStack {
491b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Thread mWorkerThread;
492b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            private final ArrayList<Runnable> mThingsToLoad;
493b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
494b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            public TaskStack() {
495b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mThingsToLoad = new ArrayList<Runnable>();
496b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mWorkerThread = new Thread(new Runnable() {
497ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev                    @Override
498b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    public void run() {
499b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        while (true) {
500b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            Runnable r = null;
501b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            synchronized (mThingsToLoad) {
502b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                if (mThingsToLoad.size() == 0) {
503b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    try {
504b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                        mThingsToLoad.wait();
505b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    } catch (InterruptedException ex) {
506b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                        // nothing to do
507b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    }
508b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                }
509b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                if (mThingsToLoad.size() > 0) {
510b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                    r = mThingsToLoad.remove(0);
511161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                                }
512161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                            }
513b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            if (r != null) {
514b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                r.run();
515161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                            }
516161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                        }
517b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
518ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev                }, "Contact.ContactsCache.TaskStack worker thread");
5198112b6847de98297068915e7d95a87a8dc842c64Todor Kalaydjiev                mWorkerThread.setPriority(Thread.MIN_PRIORITY);
520b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                mWorkerThread.start();
521b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
522b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
523b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            public void push(Runnable r) {
524b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                synchronized (mThingsToLoad) {
525b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    mThingsToLoad.add(r);
526b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    mThingsToLoad.notify();
527b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
528b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
529b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
530b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
531b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        public void pushTask(Runnable r) {
532b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            mTaskQueue.push(r);
533b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
534b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
5354f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        public Contact getMe(boolean canBlock) {
5364f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return get(SELF_ITEM_KEY, true, canBlock);
5374f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
5384f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
539b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        public Contact get(String number, boolean canBlock) {
5404f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return get(number, false, canBlock);
5414f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
5424f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
5434f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact get(String number, boolean isMe, boolean canBlock) {
5446f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
545d645c8b53ae904bc059ee1ca7232916637c223e5Tom Taylor                logWithTrace(TAG, "get(%s, %s, %s)", number, isMe, canBlock);
5466f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            }
547b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
548b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (TextUtils.isEmpty(number)) {
549b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                number = "";        // In some places (such as Korea), it's possible to receive
550b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // a message without the sender's address. In this case,
551b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // all such anonymous messages will get added to the same
552b142ea00f24f4b838e6b4d0f2761eedd0f14f625Tom Taylor                                    // thread.
553b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
554b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
555b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // Always return a Contact object, if if we don't have an actual contact
556b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // in the contacts db.
5574f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact contact = internalGet(number, isMe);
558b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Runnable r = null;
559dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
560b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (contact) {
561b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // If there's a query pending and we're willing to block then
562b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // wait here until the query completes.
563b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                while (canBlock && contact.mQueryPending) {
564b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    try {
565b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        contact.wait();
566b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    } catch (InterruptedException ex) {
567b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        // try again by virtue of the loop unless mQueryPending is false
568b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
569b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
570b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
571b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // If we're stale and we haven't already kicked off a query then kick
572b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                // it off here.
573b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (contact.mIsStale && !contact.mQueryPending) {
574b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    contact.mIsStale = false;
575b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
576b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
577dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        log("async update for " + contact.toString() + " canBlock: " + canBlock +
578b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                                " isStale: " + contact.mIsStale);
579b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
580b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
581b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    final Contact c = contact;
582b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    r = new Runnable() {
583ddd31c4011b4191035bdfbba05a8edb1785f71afTodor Kalaydjiev                        @Override
584b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        public void run() {
585b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            updateContact(c);
586161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                        }
587b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    };
588b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
589b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // set this to true while we have the lock on contact since we will
590b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // either run the query directly (canBlock case) or push the query
591b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // onto the queue.  In either case the mQueryPending will get set
592b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    // to false via updateContact.
593b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    contact.mQueryPending = true;
594b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
595b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
596b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // do this outside of the synchronized so we don't hold up any
597b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // subsequent calls to "get" on other threads
598b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (r != null) {
599b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (canBlock) {
600b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    r.run();
601b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } else {
602b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    pushTask(r);
603b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
604b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
605b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return contact;
606b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
607b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
608a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        /**
609a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * Get CacheEntry list for given phone URIs. This method will do single one query to
610a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * get expected contacts from provider. Be sure passed in URIs are not null and contains
611a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         * only valid URIs.
612a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao         */
613a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        public List<Contact> getContactInfoForPhoneUris(Parcelable[] uris) {
614a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (uris.length == 0) {
615a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return null;
616a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
617a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            StringBuilder idSetBuilder = new StringBuilder();
618a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            boolean first = true;
619a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            for (Parcelable p : uris) {
620a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                Uri uri = (Uri) p;
621a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                if ("content".equals(uri.getScheme())) {
622a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    if (first) {
623a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        first = false;
624a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        idSetBuilder.append(uri.getLastPathSegment());
625a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    } else {
626a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        idSetBuilder.append(',').append(uri.getLastPathSegment());
627a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    }
628a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
629a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
630a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            // Check whether there is content URI.
631a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (first) return null;
632a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            Cursor cursor = null;
633a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (idSetBuilder.length() > 0) {
634a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                final String whereClause = Phone._ID + " IN (" + idSetBuilder.toString() + ")";
635a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                cursor = mContext.getContentResolver().query(
636a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        PHONES_WITH_PRESENCE_URI, CALLER_ID_PROJECTION, whereClause, null, null);
637a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
638a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
639a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (cursor == null) {
640a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return null;
641a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
642a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
643a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            List<Contact> entries = new ArrayList<Contact>();
644a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
645a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            try {
646a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                while (cursor.moveToNext()) {
647a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    Contact entry = new Contact(cursor.getString(PHONE_NUMBER_COLUMN),
648a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                            cursor.getString(CONTACT_NAME_COLUMN));
649a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    fillPhoneTypeContact(entry, cursor);
650a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    ArrayList<Contact> value = new ArrayList<Contact>();
651a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    value.add(entry);
652a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    // Put the result in the cache.
653a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    mContactsHash.put(key(entry.mNumber, sStaticKeyBuffer), value);
654a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    entries.add(entry);
655a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
656a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            } finally {
657a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                cursor.close();
658a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
659a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            return entries;
660a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
661a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
662b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private boolean contactChanged(Contact orig, Contact newContactData) {
663b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // The phone number should never change, so don't bother checking.
664b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            // TODO: Maybe update it if it has gotten longer, i.e. 650-234-5678 -> +16502345678?
665b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
666a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            // Do the quick check first.
667a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (orig.mContactMethodType != newContactData.mContactMethodType) {
668b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
669b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
670b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
671a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (orig.mContactMethodId != newContactData.mContactMethodId) {
672b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
673b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
674b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
675b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (orig.mPersonId != newContactData.mPersonId) {
6766f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
6776f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                    Log.d(TAG, "person id changed");
6786f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                }
679b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
680b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
681b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
682b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (orig.mPresenceResId != newContactData.mPresenceResId) {
6836f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
6846f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                    Log.d(TAG, "presence changed");
6856f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                }
686b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
687b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
688b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
6891b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor            if (orig.mSendToVoicemail != newContactData.mSendToVoicemail) {
6901b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                return true;
6911b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor            }
6921b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor
693a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String oldName = emptyIfNull(orig.mName);
694a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String newName = emptyIfNull(newContactData.mName);
695a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (!oldName.equals(newName)) {
6966f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
6976f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                    Log.d(TAG, String.format("name changed: %s -> %s", oldName, newName));
6986f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                }
699a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return true;
700a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
701a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
702a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String oldLabel = emptyIfNull(orig.mLabel);
703a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            String newLabel = emptyIfNull(newContactData.mLabel);
704a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            if (!oldLabel.equals(newLabel)) {
7056f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
7066f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                    Log.d(TAG, String.format("label changed: %s -> %s", oldLabel, newLabel));
7076f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                }
708a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                return true;
709a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
710a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
711b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (!Arrays.equals(orig.mAvatarData, newContactData.mAvatarData)) {
7126f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
7136f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                    Log.d(TAG, "avatar changed");
7146f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                }
715b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return true;
716b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
717b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
718b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return false;
719b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
720b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
721b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private void updateContact(final Contact c) {
722b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (c == null) {
723b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return;
724b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
725b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
7264f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact entry = getContactInfo(c);
727dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            synchronized (c) {
728b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (contactChanged(c, entry)) {
729b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
730b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        log("updateContact: contact changed for " + entry.mName);
731161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                    }
732b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
733b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mNumber = entry.mNumber;
734b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mLabel = entry.mLabel;
735b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPersonId = entry.mPersonId;
736b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPresenceResId = entry.mPresenceResId;
737b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mPresenceText = entry.mPresenceText;
738b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mAvatarData = entry.mAvatarData;
739b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mAvatar = entry.mAvatar;
740a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    c.mContactMethodId = entry.mContactMethodId;
741a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                    c.mContactMethodType = entry.mContactMethodType;
7424759133e3c935e751815bf4f997d029caa64d889Bai Tao                    c.mNumberE164 = entry.mNumberE164;
7434f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    c.mName = entry.mName;
7441b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                    c.mSendToVoicemail = entry.mSendToVoicemail;
745dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
746dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    c.notSynchronizedUpdateNameAndNumber();
747b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
748ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // We saw a bug where we were updating an empty contact. That would trigger
749ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // l.onUpdate() below, which would call ComposeMessageActivity.onUpdate,
750ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // which would call the adapter's notifyDataSetChanged, which would throw
751ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // away the message items and rebuild, eventually calling updateContact()
752ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // again -- all in a vicious and unending loop. Break the cycle and don't
753ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    // notify if the number (the most important piece of information) is empty.
754ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                    if (!TextUtils.isEmpty(c.mNumber)) {
755ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // clone the list of listeners in case the onUpdate call turns around and
756ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // modifies the list of listeners
757ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        // access to mListeners is synchronized on ContactsCache
758ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        HashSet<UpdateListener> iterator;
759ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        synchronized (mListeners) {
760ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                            iterator = (HashSet<UpdateListener>)Contact.mListeners.clone();
761ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        }
762ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        for (UpdateListener l : iterator) {
7636f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                            if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
7646f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                                Log.d(TAG, "updating " + l);
7656f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                            }
766ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                            l.onUpdate(c);
767ad5cd4c952745600c8b5a0ca06455f5bae0dacddTom Taylor                        }
768b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
769b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
770b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                synchronized (c) {
771b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.mQueryPending = false;
772b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    c.notifyAll();
773161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                }
774b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
775161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner        }
776161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner
777b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
778b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Returns the caller info in Contact.
779b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
7804f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact getContactInfo(Contact c) {
7814f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if (c.mIsMe) {
7824f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForSelf();
7834f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            } else if (Mms.isEmailAddress(c.mNumber) || isAlphaNumber(c.mNumber)) {
7844f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForEmailAddress(c.mNumber);
785b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } else {
7864f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return getContactInfoForPhoneNumber(c.mNumber);
787b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
78833a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick        }
78933a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick
7906ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Some received sms's have addresses such as "OakfieldCPS" or "T-Mobile". This
7916ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // function will attempt to identify these and return true. If the number contains
7926ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // 3 or more digits, such as "jello123", this function will return false.
7936ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Some countries have 3 digits shortcodes and we have to identify them as numbers.
7946ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    http://en.wikipedia.org/wiki/Short_code
7956ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        // Examples of input/output for this function:
7966ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Jello123" -> false  [3 digits, it is considered to be the phone number "123"]
7976ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "T-Mobile" -> true   [it is considered to be the address "T-Mobile"]
7986ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Mobile1"  -> true   [1 digit, it is considered to be the address "Mobile1"]
7996ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "Dogs77"   -> true   [2 digits, it is considered to be the address "Dogs77"]
8006ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "****1"    -> true   [1 digits, it is considered to be the address "****1"]
8016ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "#4#5#6#"  -> true   [it is considered to be the address "#4#5#6#"]
8026ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "AB12"     -> true   [2 digits, it is considered to be the address "AB12"]
8036ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        //    "12"       -> true   [2 digits, it is considered to be the address "12"]
8046ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        private boolean isAlphaNumber(String number) {
8056ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // TODO: PhoneNumberUtils.isWellFormedSmsAddress() only check if the number is a valid
8066ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // GSM SMS address. If the address contains a dialable char, it considers it a well
8076ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // formed SMS addr. CDMA doesn't work that way and has a different parser for SMS
8086ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // address (see CdmaSmsAddress.parse(String address)). We should definitely fix this!!!
8096ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            if (!PhoneNumberUtils.isWellFormedSmsAddress(number)) {
8106ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                // The example "T-Mobile" will exit here because there are no numbers.
8116ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                return true;        // we're not an sms address, consider it an alpha number
8126ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            }
813d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor            if (MessageUtils.isAlias(number)) {
814d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor                return true;
815d7fddfa20e7bfe9f0bf376f9cce9188bf9bf0354Tom Taylor            }
8166ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            number = PhoneNumberUtils.extractNetworkPortion(number);
8176ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            if (TextUtils.isEmpty(number)) {
8186ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor                return true;    // there are no digits whatsoever in the number
8196ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            }
8206ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // At this point, anything like "Mobile1" or "Dogs77" will be stripped down to
8216ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            // "1" and "77". "#4#5#6#" remains as "#4#5#6#" at this point.
8226ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor            return number.length() < 3;
8236ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor        }
8246ac26ca62ba1e4252bf93672abd370966d3fb7c3Tom Taylor
825b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
826b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Queries the caller id info with the phone number.
827b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * @return a Contact containing the caller id info corresponding to the number.
828b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
829b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private Contact getContactInfoForPhoneNumber(String number) {
830b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            number = PhoneNumberUtils.stripSeparators(number);
831b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Contact entry = new Contact(number);
832a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            entry.mContactMethodType = CONTACT_METHOD_TYPE_PHONE;
833b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8346f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
8356f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                log("queryContactInfoByNumber: number=" + number);
8366f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            }
837b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8384759133e3c935e751815bf4f997d029caa64d889Bai Tao            String normalizedNumber = PhoneNumberUtils.normalizeNumber(number);
8394759133e3c935e751815bf4f997d029caa64d889Bai Tao            String minMatch = PhoneNumberUtils.toCallerIDMinMatch(normalizedNumber);
8404759133e3c935e751815bf4f997d029caa64d889Bai Tao            if (!TextUtils.isEmpty(normalizedNumber) && !TextUtils.isEmpty(minMatch)) {
8414759133e3c935e751815bf4f997d029caa64d889Bai Tao                String numberLen = String.valueOf(normalizedNumber.length());
8424759133e3c935e751815bf4f997d029caa64d889Bai Tao                String numberE164 = PhoneNumberUtils.formatNumberToE164(
8434759133e3c935e751815bf4f997d029caa64d889Bai Tao                        number, MmsApp.getApplication().getCurrentCountryIso());
8444759133e3c935e751815bf4f997d029caa64d889Bai Tao                String selection;
8454759133e3c935e751815bf4f997d029caa64d889Bai Tao                String[] args;
8464759133e3c935e751815bf4f997d029caa64d889Bai Tao                if (TextUtils.isEmpty(numberE164)) {
8474759133e3c935e751815bf4f997d029caa64d889Bai Tao                    selection = CALLER_ID_SELECTION_WITHOUT_E164;
8484759133e3c935e751815bf4f997d029caa64d889Bai Tao                    args = new String[] {minMatch, numberLen, normalizedNumber, numberLen};
8494759133e3c935e751815bf4f997d029caa64d889Bai Tao                } else {
8504759133e3c935e751815bf4f997d029caa64d889Bai Tao                    selection = CALLER_ID_SELECTION;
8514759133e3c935e751815bf4f997d029caa64d889Bai Tao                    args = new String[] {
8524759133e3c935e751815bf4f997d029caa64d889Bai Tao                            minMatch, numberE164, numberLen, normalizedNumber, numberLen};
8534759133e3c935e751815bf4f997d029caa64d889Bai Tao                }
854b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8554759133e3c935e751815bf4f997d029caa64d889Bai Tao                Cursor cursor = mContext.getContentResolver().query(
8564759133e3c935e751815bf4f997d029caa64d889Bai Tao                        PHONES_WITH_PRESENCE_URI, CALLER_ID_PROJECTION, selection, args, null);
8574759133e3c935e751815bf4f997d029caa64d889Bai Tao                if (cursor == null) {
8584759133e3c935e751815bf4f997d029caa64d889Bai Tao                    Log.w(TAG, "queryContactInfoByNumber(" + number + ") returned NULL cursor!"
8594759133e3c935e751815bf4f997d029caa64d889Bai Tao                            + " contact uri used " + PHONES_WITH_PRESENCE_URI);
8604759133e3c935e751815bf4f997d029caa64d889Bai Tao                    return entry;
8614759133e3c935e751815bf4f997d029caa64d889Bai Tao                }
862b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
8634759133e3c935e751815bf4f997d029caa64d889Bai Tao                try {
8644759133e3c935e751815bf4f997d029caa64d889Bai Tao                    if (cursor.moveToFirst()) {
8654759133e3c935e751815bf4f997d029caa64d889Bai Tao                        fillPhoneTypeContact(entry, cursor);
8664759133e3c935e751815bf4f997d029caa64d889Bai Tao                    }
8674759133e3c935e751815bf4f997d029caa64d889Bai Tao                } finally {
8684759133e3c935e751815bf4f997d029caa64d889Bai Tao                    cursor.close();
869b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
870b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
871b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return entry;
87233a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick        }
87333a87f96f8c625aa10131a77a3968c97c4ec5a62Ficus Kirkpatrick
8744f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        /**
8754f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor         * @return a Contact containing the info for the profile.
8764f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor         */
8774f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact getContactInfoForSelf() {
8784f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Contact entry = new Contact(true);
8794f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            entry.mContactMethodType = CONTACT_METHOD_TYPE_SELF;
8804f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
8816f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
8826f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                log("getContactInfoForSelf");
8836f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            }
8844f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Cursor cursor = mContext.getContentResolver().query(
8854f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    Profile.CONTENT_URI, SELF_PROJECTION, null, null, null);
8864f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if (cursor == null) {
8874f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Log.w(TAG, "getContactInfoForSelf() returned NULL cursor!"
8884f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        + " contact uri used " + Profile.CONTENT_URI);
8894f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                return entry;
8904f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
8914f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
8924f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            try {
8934f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                if (cursor.moveToFirst()) {
8944f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    fillSelfContact(entry, cursor);
8954f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
8964f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            } finally {
8974f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                cursor.close();
8984f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
8994f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            return entry;
9004f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
9014f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
902a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        private void fillPhoneTypeContact(final Contact contact, final Cursor cursor) {
903a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            synchronized (contact) {
904a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mContactMethodType = CONTACT_METHOD_TYPE_PHONE;
905a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mContactMethodId = cursor.getLong(PHONE_ID_COLUMN);
906a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mLabel = cursor.getString(PHONE_LABEL_COLUMN);
907a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mName = cursor.getString(CONTACT_NAME_COLUMN);
908a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPersonId = cursor.getLong(CONTACT_ID_COLUMN);
909a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPresenceResId = getPresenceIconResourceId(
910a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                        cursor.getInt(CONTACT_PRESENCE_COLUMN));
911a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mPresenceText = cursor.getString(CONTACT_STATUS_COLUMN);
9124759133e3c935e751815bf4f997d029caa64d889Bai Tao                contact.mNumberE164 = cursor.getString(PHONE_NORMALIZED_NUMBER);
9131b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                contact.mSendToVoicemail = cursor.getInt(SEND_TO_VOICEMAIL) == 1;
9146f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
9154f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    log("fillPhoneTypeContact: name=" + contact.mName + ", number="
9161b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                            + contact.mNumber + ", presence=" + contact.mPresenceResId
9171b8df494e49bf934fe160baea54ab5afc2ff3100Tom Taylor                            + " SendToVoicemail: " + contact.mSendToVoicemail);
918a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                }
919a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
920a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            byte[] data = loadAvatarData(contact);
921a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao
922a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            synchronized (contact) {
923a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao                contact.mAvatarData = data;
924a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            }
925a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao        }
9264f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
9274f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private void fillSelfContact(final Contact contact, final Cursor cursor) {
9284f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            synchronized (contact) {
9294f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                contact.mName = cursor.getString(SELF_NAME_COLUMN);
9304f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                if (TextUtils.isEmpty(contact.mName)) {
9314f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    contact.mName = mContext.getString(R.string.messagelist_sender_self);
9324f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
9336f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
9344f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                    log("fillSelfContact: name=" + contact.mName + ", number="
9354f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                            + contact.mNumber);
9364f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                }
9374f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
9384f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            byte[] data = loadAvatarData(contact);
9394f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor
9404f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            synchronized (contact) {
9414f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                contact.mAvatarData = data;
9424f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            }
9434f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        }
944b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /*
945b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Load the avatar data from the cursor into memory.  Don't decode the data
946b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * until someone calls for it (see getAvatar).  Hang onto the raw data so that
947b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * we can compare it when the data is reloaded.
948b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * TODO: consider comparing a checksum so that we don't have to hang onto
949b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * the raw bytes after the image is decoded.
950b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
951dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang        private byte[] loadAvatarData(Contact entry) {
952dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            byte [] data = null;
953dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
9544f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            if ((!entry.mIsMe && entry.mPersonId == 0) || entry.mAvatar != null) {
955dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                return null;
956b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
957b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
9586f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor            if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
9598d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                log("loadAvatarData: name=" + entry.mName + ", number=" + entry.mNumber);
960313d09ee558e6ac94ab28a205357885dc830cd0dTom Taylor            }
961313d09ee558e6ac94ab28a205357885dc830cd0dTom Taylor
9628d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor            // If the contact is "me", then use my local profile photo. Otherwise, build a
9638d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor            // uri to get the avatar of the contact.
9644f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor            Uri contactUri = entry.mIsMe ?
9658d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                    Profile.CONTENT_URI :
9668d54acc063c03b42c9b10dbd013a1552ebbe2d9fTom Taylor                    ContentUris.withAppendedId(Contacts.CONTENT_URI, entry.mPersonId);
967b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
968dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            InputStream avatarDataStream = Contacts.openContactPhotoInputStream(
969b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        mContext.getContentResolver(),
970b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        contactUri);
971b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            try {
972b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                if (avatarDataStream != null) {
973dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    data = new byte[avatarDataStream.available()];
974b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    avatarDataStream.read(data, 0, data.length);
975b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
976b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } catch (IOException ex) {
977b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                //
978b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            } finally {
979b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                try {
980b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    if (avatarDataStream != null) {
981b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        avatarDataStream.close();
982b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
983b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } catch (IOException e) {
984b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                }
985b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
986dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
987dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang            return data;
9881d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
9891d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
990b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private int getPresenceIconResourceId(int presence) {
9916be18bedb5b87dbbcdb54f37d5a0945bd0f71377Tom Taylor            // TODO: must fix for SDK
992b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (presence != Presence.OFFLINE) {
993b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                return Presence.getPresenceIconResourceId(presence);
994b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            }
995b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
996b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return 0;
997b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        }
998b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
999b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        /**
1000b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         * Query the contact email table to get the name of an email address.
1001b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner         */
1002b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        private Contact getContactInfoForEmailAddress(String email) {
1003b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Contact entry = new Contact(email);
1004a6dd14dc0963aa8f30e625aa88e925e3fa0744f0Bai Tao            entry.mContactMethodType = CONTACT_METHOD_TYPE_EMAIL;
1005b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
1006b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            Cursor cursor = SqliteWrapper.query(mContext, mContext.getContentResolver(),
1007b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_WITH_PRESENCE_URI,
1008b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_PROJECTION,
1009b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    EMAIL_SELECTION,
1010dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                    new String[] { email },
1011b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    null);
1012b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner
1013b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            if (cursor != null) {
1014b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                try {
1015b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    while (cursor.moveToNext()) {
1016dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        boolean found = false;
1017dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        synchronized (entry) {
10180cf7a7c3bf1e23e03933eddb9bfc4b67f568fc16Tom Taylor                            entry.mContactMethodId = cursor.getLong(EMAIL_ID_COLUMN);
1019dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            entry.mPresenceResId = getPresenceIconResourceId(
1020dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                    cursor.getInt(EMAIL_STATUS_COLUMN));
10210cf7a7c3bf1e23e03933eddb9bfc4b67f568fc16Tom Taylor                            entry.mPersonId = cursor.getLong(EMAIL_CONTACT_ID_COLUMN);
10220cf7a7c3bf1e23e03933eddb9bfc4b67f568fc16Tom Taylor                            entry.mSendToVoicemail =
10230cf7a7c3bf1e23e03933eddb9bfc4b67f568fc16Tom Taylor                                    cursor.getInt(EMAIL_SEND_TO_VOICEMAIL_COLUMN) == 1;
1024dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1025dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            String name = cursor.getString(EMAIL_NAME_COLUMN);
1026dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            if (TextUtils.isEmpty(name)) {
1027dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                name = cursor.getString(EMAIL_CONTACT_NAME_COLUMN);
1028dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            }
1029dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            if (!TextUtils.isEmpty(name)) {
1030dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                entry.mName = name;
10316f21719c32796092cb2d07a3d375633a1f3c4c6bTom Taylor                                if (Log.isLoggable(LogTag.CONTACT, Log.DEBUG)) {
1032dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                    log("getContactInfoForEmailAddress: name=" + entry.mName +
1033dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                            ", email=" + email + ", presence=" +
1034dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                            entry.mPresenceResId);
1035dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                }
1036dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                found = true;
1037dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            }
1038b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        }
1039dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1040dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        if (found) {
1041dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            byte[] data = loadAvatarData(entry);
1042dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            synchronized (entry) {
1043dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                                entry.mAvatarData = data;
1044b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            }
1045dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang
1046b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                            break;
1047b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                        }
1048b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    }
1049b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                } finally {
1050b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner                    cursor.close();
10511d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
10521d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
1053b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            return entry;
10541d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
10551d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1056513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // Invert and truncate to five characters the phoneNumber so that we
1057513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // can use it as the key in a hashtable.  We keep a mapping of this
1058513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // key to a list of all contacts which have the same key.
105940ab55a4337738075adbda3642ce0415f3810c4cMark Wagner        private String key(String phoneNumber, CharBuffer keyBuffer) {
1060513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.clear();
1061513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.mark();
1062513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
1063513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            int position = phoneNumber.length();
1064513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            int resultCount = 0;
1065513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            while (--position >= 0) {
1066513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                char c = phoneNumber.charAt(position);
1067513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                if (Character.isDigit(c)) {
1068513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    keyBuffer.put(c);
1069513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    if (++resultCount == STATIC_KEY_BUFFER_MAXIMUM_LENGTH) {
1070513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        break;
10711d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    }
10721d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
1073513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            }
1074513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            keyBuffer.reset();
1075513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            if (resultCount > 0) {
107640ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                return keyBuffer.toString();
1077513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner            } else {
1078513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // there were no usable digits in the input phoneNumber
1079513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                return phoneNumber;
10801d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
10811d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
10821d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1083513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // Reuse this so we don't have to allocate each time we go through this
1084513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        // "get" function.
1085513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        static final int STATIC_KEY_BUFFER_MAXIMUM_LENGTH = 5;
1086513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner        static CharBuffer sStaticKeyBuffer = CharBuffer.allocate(STATIC_KEY_BUFFER_MAXIMUM_LENGTH);
10871d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
10884f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor        private Contact internalGet(String numberOrEmail, boolean isMe) {
1089b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
1090513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // See if we can find "number" in the hashtable.
1091513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                // If so, just return the result.
10924f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                final boolean isNotRegularPhoneNumber = isMe || Mms.isEmailAddress(numberOrEmail) ||
1093dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        MessageUtils.isAlias(numberOrEmail);
1094dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                final String key = isNotRegularPhoneNumber ?
1095dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        numberOrEmail : key(numberOrEmail, sStaticKeyBuffer);
1096513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner
1097513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                ArrayList<Contact> candidates = mContactsHash.get(key);
1098513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                if (candidates != null) {
1099513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    int length = candidates.size();
1100513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (int i = 0; i < length; i++) {
1101513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        Contact c= candidates.get(i);
1102513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        if (isNotRegularPhoneNumber) {
1103513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            if (numberOrEmail.equals(c.mNumber)) {
1104513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                                return c;
1105513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            }
1106513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        } else {
1107513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            if (PhoneNumberUtils.compare(numberOrEmail, c.mNumber)) {
1108513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                                return c;
1109513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                            }
1110513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                        }
11111d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                    }
1112513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                } else {
1113513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    candidates = new ArrayList<Contact>();
1114513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    // call toString() since it may be the static CharBuffer
111540ab55a4337738075adbda3642ce0415f3810c4cMark Wagner                    mContactsHash.put(key, candidates);
11161d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick                }
11174f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                Contact c = isMe ?
11184f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        new Contact(true) :
11194f1871e8e85598c94d5f09d508c690c41fdcce16Tom Taylor                        new Contact(numberOrEmail);
1120513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                candidates.add(c);
1121161c3cfe3480ab8b1b585cc0d3601f0cacc28c6dMark Wagner                return c;
11221d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick            }
11231d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick        }
11241d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick
1125b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner        void invalidate() {
1126d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            // Don't remove the contacts. Just mark them stale so we'll update their
1127d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            // info, particularly their presence.
1128b54460a3af15ca85c2a7b6d5422e9e5eb4592fa8Mark Wagner            synchronized (ContactsCache.this) {
1129513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                for (ArrayList<Contact> alc : mContactsHash.values()) {
1130513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    for (Contact c : alc) {
1131dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        synchronized (c) {
1132dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                            c.mIsStale = true;
1133dc7f95eaf3f1668e90e1add17e07fd3b4b839535Wei Huang                        }
1134513ec194e86409bac7d2ca61a4edcbbe6a36181eMark Wagner                    }
1135d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor                }
1136d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor            }
1137d09e218e2278acb6276207bddfbef1761617e8f8Tom Taylor        }
11381b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor
11391b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor        // Remove a contact from the ContactsCache based on the number or email address
11401b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor        private void remove(Contact contact) {
11411b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor            synchronized (ContactsCache.this) {
11421b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                String number = contact.getNumber();
11431b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                final boolean isNotRegularPhoneNumber = contact.isMe() ||
11441b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                    Mms.isEmailAddress(number) ||
11451b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                    MessageUtils.isAlias(number);
11461b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                final String key = isNotRegularPhoneNumber ?
11471b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        number : key(number, sStaticKeyBuffer);
11481b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                ArrayList<Contact> candidates = mContactsHash.get(key);
11491b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                if (candidates != null) {
11501b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                    int length = candidates.size();
11511b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                    for (int i = 0; i < length; i++) {
11521b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        Contact c = candidates.get(i);
11531b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        if (isNotRegularPhoneNumber) {
11541b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                            if (number.equals(c.mNumber)) {
11551b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                candidates.remove(i);
11561b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                break;
11571b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                            }
11581b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        } else {
11591b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                            if (PhoneNumberUtils.compare(number, c.mNumber)) {
11601b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                candidates.remove(i);
11611b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                                break;
11621b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                            }
11631b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        }
11641b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                    }
11651b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                    if (candidates.size() == 0) {
11661b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                        mContactsHash.remove(key);
11671b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                    }
11681b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor                }
11691b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor            }
11701b1af7c751218e380bd119629ff9a652157fbb21Tom Taylor        }
117132a1dcecca6ed0881431555a4218d0282814326cWink Saville    }
117232a1dcecca6ed0881431555a4218d0282814326cWink Saville
117332a1dcecca6ed0881431555a4218d0282814326cWink Saville    private static void log(String msg) {
117432a1dcecca6ed0881431555a4218d0282814326cWink Saville        Log.d(TAG, msg);
11751d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick    }
11761d98ae0b203e01034ddead4214d1520ce863a23bFicus Kirkpatrick}
1177