19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2006 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage com.android.internal.telephony;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.Context;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.database.Cursor;
21265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawaimport android.graphics.Bitmap;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.drawable.Drawable;
2394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brownimport android.location.CountryDetector;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.Uri;
253c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.Phone;
2685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brownimport android.provider.ContactsContract.Data;
2785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brownimport android.provider.ContactsContract.PhoneLookup;
2885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brownimport android.provider.ContactsContract.RawContacts;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.telephony.PhoneNumberUtils;
3085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brownimport android.telephony.TelephonyManager;
3185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brownimport android.text.TextUtils;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
34e713576292fc72086de47066981b86ad2f27ab0fShaopeng Jiaimport com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
35e713576292fc72086de47066981b86ad2f27ab0fShaopeng Jiaimport com.android.i18n.phonenumbers.NumberParseException;
36e713576292fc72086de47066981b86ad2f27ab0fShaopeng Jiaimport com.android.i18n.phonenumbers.PhoneNumberUtil;
37e713576292fc72086de47066981b86ad2f27ab0fShaopeng Jiaimport com.android.i18n.phonenumbers.Phonenumber.PhoneNumber;
3894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
3994202fe1217b9f63e1f5c314379a9f0021e96ea8David Brownimport java.util.Locale;
4094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
412e27a0be78bd9510785789caa14baa029051ca20Wink Saville
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Looks up caller information for the given phone number.
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * {@hide}
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class CallerInfo {
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String TAG = "CallerInfo";
492e27a0be78bd9510785789caa14baa029051ca20Wink Saville    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String UNKNOWN_NUMBER = "-1";
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final String PRIVATE_NUMBER = "-2";
53105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    public static final String PAYPHONE_NUMBER = "-3";
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Please note that, any one of these member variables can be null,
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and any accesses to them should be prepared to handle such a case.
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Also, it is implied that phoneNumber is more often populated than
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * name is, (think of calls being dialed/received using numbers where
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * names are not known to the device), so phoneNumber should serve as
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a dependable fallback when name is unavailable.
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * One other detail here is that this CallerInfo object reflects
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * information found on a connection, it is an OUTPUT that serves
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * mainly to display information to the user.  In no way is this object
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used as input to make a connection, so we can choose to display
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * whatever human-readable text makes sense to the user for a
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * connection.  This is especially relevant for the phone number field,
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * since it is the one field that is most likely exposed to the user.
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * As an example:
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *   1. User dials "911"
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *   2. Device recognizes that this is an emergency number
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *   3. We use the "Emergency Number" string instead of "911" in the
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *     phoneNumber field.
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * What we're really doing here is treating phoneNumber as an essential
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * field here, NOT name.  We're NOT always guaranteed to have a name
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for a connection, but the number should be displayable.
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String name;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String phoneNumber;
8494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    public String normalizedNumber;
8594202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    public String geoDescription;
86dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville
87dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville    public String cnapName;
88dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville    public int numberPresentation;
89dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville    public int namePresentation;
90dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville    public boolean contactExists;
91dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String phoneLabel;
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /* Split up the phoneLabel into number type and label name */
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int    numberType;
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String numberLabel;
962563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public int photoResource;
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public long person_id;
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean needUpdate;
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Uri contactRefUri;
1012563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
1022563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville    // fields to hold individual contact preference data,
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // including the send to voicemail flag and the ringtone
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // uri reference.
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Uri contactRingtoneUri;
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean shouldSendToVoicemail;
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Drawable representing the caller image.  This is essentially
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a cache for the image data tied into the connection /
111265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * callerinfo object.
112265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     *
113265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * This might be a high resolution picture which is more suitable
114265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * for full-screen image view than for smaller icons used in some
115265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * kinds of notifications.
116265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     *
117265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * The {@link #isCachedPhotoCurrent} flag indicates if the image
118265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * data needs to be reloaded.
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public Drawable cachedPhoto;
121265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa    /**
122265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * Bitmap representing the caller image which has possibly lower
123265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * resolution than {@link #cachedPhoto} and thus more suitable for
124265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * icons (like notification icons).
125265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     *
126265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * In usual cases this is just down-scaled image of {@link #cachedPhoto}.
127265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * If the down-scaling fails, this will just become null.
128265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     *
129265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * The {@link #isCachedPhotoCurrent} flag indicates if the image
130265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * data needs to be reloaded.
131265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     */
132265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa    public Bitmap cachedPhotoIcon;
133265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa    /**
134265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * Boolean which indicates if {@link #cachedPhoto} and
135265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * {@link #cachedPhotoIcon} is fresh enough. If it is false,
136265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     * those images aren't pointing to valid objects.
137265974803a01b6a461a94f984bb6b1e5f1f48496Daisuke Miyakawa     */
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean isCachedPhotoCurrent;
1399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
140e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    private boolean mIsEmergency;
14160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    private boolean mIsVoiceMail;
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public CallerInfo() {
144e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        // TODO: Move all the basic initialization here?
145e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        mIsEmergency = false;
14660d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        mIsVoiceMail = false;
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * getCallerInfo given a Cursor.
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the context used to retrieve string constants
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param contactRef the URI to attach to this CallerInfo object
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param cursor the first object in the cursor is used to build the CallerInfo object.
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the CallerInfo which contains the caller id for the given
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * number. The returned CallerInfo is null if no number is supplied.
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CallerInfo info = new CallerInfo();
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.photoResource = 0;
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.phoneLabel = null;
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.numberType = 0;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.numberLabel = null;
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.cachedPhoto = null;
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.isCachedPhotoCurrent = false;
165dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville        info.contactExists = false;
1662563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
16794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        if (VDBG) Log.v(TAG, "getCallerInfo() based on cursor...");
1682563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (cursor != null) {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (cursor.moveToFirst()) {
171c72509b4d815e23bfa563cfe96e04f54f2a221feNicolas Catania                // TODO: photo_id is always available but not taken
172c72509b4d815e23bfa563cfe96e04f54f2a221feNicolas Catania                // care of here. Maybe we should store it in the
173c72509b4d815e23bfa563cfe96e04f54f2a221feNicolas Catania                // CallerInfo object as well.
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int columnIndex;
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Look for the name
1783c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME);
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (columnIndex != -1) {
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    info.name = cursor.getString(columnIndex);
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Look for the number
1843c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER);
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (columnIndex != -1) {
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    info.phoneNumber = cursor.getString(columnIndex);
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1882563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
1896a3d188f18b5ae278c802c8bbd1e0a44da555cdfBai Tao                // Look for the normalized number
1906a3d188f18b5ae278c802c8bbd1e0a44da555cdfBai Tao                columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER);
1916a3d188f18b5ae278c802c8bbd1e0a44da555cdfBai Tao                if (columnIndex != -1) {
19294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    info.normalizedNumber = cursor.getString(columnIndex);
1936a3d188f18b5ae278c802c8bbd1e0a44da555cdfBai Tao                }
1946a3d188f18b5ae278c802c8bbd1e0a44da555cdfBai Tao
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // Look for the label/type combo
1963c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL);
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (columnIndex != -1) {
1983c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                    int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE);
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (typeColumnIndex != -1) {
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        info.numberType = cursor.getInt(typeColumnIndex);
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        info.numberLabel = cursor.getString(columnIndex);
2023c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                        info.phoneLabel = Phone.getDisplayLabel(context,
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                info.numberType, info.numberLabel)
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                .toString();
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
20885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                // Look for the person_id.
20985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                columnIndex = getColumnIndexForPersonId(contactRef, cursor);
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (columnIndex != -1) {
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    info.person_id = cursor.getLong(columnIndex);
21285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    if (VDBG) Log.v(TAG, "==> got info.person_id: " + info.person_id);
213c72509b4d815e23bfa563cfe96e04f54f2a221feNicolas Catania                } else {
21485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    // No valid columnIndex, so we can't look up person_id.
21585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    Log.w(TAG, "Couldn't find person_id column for " + contactRef);
21685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    // Watch out: this means that anything that depends on
21785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    // person_id will be broken (like contact photo lookups in
21885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                    // the in-call UI, for example.)
2199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2202563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // look for the custom ringtone, create from the string stored
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // in the database.
2233c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE);
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    info.contactRingtoneUri = null;
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // look for the send to voicemail flag, set it to true only
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // under certain circumstances.
2323c513ed95cee2e0bcd7208cb7e46307f09c907c9Dmitri Plotnikov                columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL);
2332563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville                info.shouldSendToVoicemail = (columnIndex != -1) &&
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        ((cursor.getInt(columnIndex)) == 1);
235dda5391d5079537e275c9f4ed2637a1484d0e4e8Wink Saville                info.contactExists = true;
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            cursor.close();
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.needUpdate = false;
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.name = normalize(info.name);
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        info.contactRefUri = contactRef;
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return info;
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2462563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * getCallerInfo given a URI, look up in the call-log database
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for the uri unique key.
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the context used to get the ContentResolver
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param contactRef the URI used to lookup caller id
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the CallerInfo which contains the caller id for the given
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * number. The returned CallerInfo is null if no number is supplied.
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CallerInfo getCallerInfo(Context context, Uri contactRef) {
2562563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2572563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville        return getCallerInfo(context, contactRef,
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                context.getContentResolver().query(contactRef, null, null, null, null));
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2602563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * getCallerInfo given a phone number, look up in the call-log database
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * for the matching caller id info.
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the context used to get the ContentResolver
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param number the phone number used to lookup caller id
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return the CallerInfo which contains the caller id for the given
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * number. The returned CallerInfo is null if no number is supplied. If
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * a matching number is not found, then a generic caller info is returned,
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * with all relevant fields empty or null.
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static CallerInfo getCallerInfo(Context context, String number) {
27294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        if (VDBG) Log.v(TAG, "getCallerInfo() based on number...");
27394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TextUtils.isEmpty(number)) {
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
27660d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        }
27760d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
27860d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // Change the callerInfo number ONLY if it is an emergency number
27960d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // or if it is the voicemail number.  If it is either, take a
28060d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // shortcut and skip the query.
2816b7c3f8a1cd8b638defc28a3249746e99b8039aeShaopeng Jia        if (PhoneNumberUtils.isLocalEmergencyNumber(number, context)) {
28260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            return new CallerInfo().markAsEmergency(context);
28360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        } else if (PhoneNumberUtils.isVoiceMailNumber(number)) {
28460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            return new CallerInfo().markAsVoiceMail();
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
28784b4d37dd1e9269f73c2a9cacadcd88ec4256e3fDmitri Plotnikov        Uri contactUri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
2882563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CallerInfo info = getCallerInfo(context, contactUri);
2906fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan        info = doSecondaryLookupIfNecessary(context, number, info);
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2922563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville        // if no query results were returned with a viable number,
2932563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville        // fill in the original number value we used to query with.
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (TextUtils.isEmpty(info.phoneNumber)) {
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            info.phoneNumber = number;
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2972563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return info;
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3026fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * Performs another lookup if previous lookup fails and it's a SIP call
3036fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * and the peer's username is all numeric. Look up the username as it
3046fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * could be a PSTN number in the contact database.
3056fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     *
3066fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * @param context the query context
3076fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * @param number the original phone number, could be a SIP URI
3086fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * @param previousResult the result of previous lookup
3096fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     * @return previousResult if it's not the case
3106fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan     */
3116fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan    static CallerInfo doSecondaryLookupIfNecessary(Context context,
3126fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan            String number, CallerInfo previousResult) {
3136fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan        if (!previousResult.contactExists
3146fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan                && PhoneNumberUtils.isUriNumber(number)) {
315158f116eb7fdc23a12d6822d34a549f33605bc8cDavid Brown            String username = PhoneNumberUtils.getUsernameFromUriNumber(number);
3166fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan            if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
3176fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan                previousResult = getCallerInfo(context,
3186fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan                        Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI,
3196fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan                                Uri.encode(username)));
3206fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan            }
3216fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan        }
3226fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan        return previousResult;
3236fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan    }
3246fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan
3256fe795ecd35c4d49822d349424fc71b660577dfcHung-ying Tyan    /**
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * getCallerId: a convenience method to get the caller id for a given
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * number.
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param context the context used to get the ContentResolver.
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param number a phone number.
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return if the number belongs to a contact, the contact's name is
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * returned; otherwise, the number itself is returned.
3332563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville     *
3342563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville     * TODO NOTE: This MAY need to refer to the Asynchronous Query API
3352563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville     * [startQuery()], instead of getCallerInfo, but since it looks like
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * it is only being used by the provider calls in the messaging app:
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *   1. android.provider.Telephony.Mms.getDisplayAddress()
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *   2. android.provider.Telephony.Sms.getDisplayAddress()
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * We may not need to make the change.
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String getCallerId(Context context, String number) {
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        CallerInfo info = getCallerInfo(context, number);
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String callerID = null;
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (info != null) {
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String name = info.name;
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (!TextUtils.isEmpty(name)) {
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callerID = name;
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                callerID = number;
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return callerID;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
358e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // Accessors
359e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania
360e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    /**
361e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     * @return true if the caller info is an emergency number.
362e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     */
363e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    public boolean isEmergencyNumber() {
364e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        return mIsEmergency;
365e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    }
366e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania
367e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    /**
36860d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * @return true if the caller info is a voicemail number.
36960d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     */
37060d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    public boolean isVoiceMailNumber() {
37160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        return mIsVoiceMail;
37260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    }
37360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
37460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    /**
375e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     * Mark this CallerInfo as an emergency call.
376e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     * @param context To lookup the localized 'Emergency Number' string.
377e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     * @return this instance.
378e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania     */
379e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // TODO: Note we're setting the phone number here (refer to
380e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // javadoc comments at the top of CallerInfo class) to a localized
381e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // string 'Emergency Number'. This is pretty bad because we are
382e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // making UI work here instead of just packaging the data. We
383e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // should set the phone number to the dialed number and name to
384e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // 'Emergency Number' and let the UI make the decision about what
385e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    // should be displayed.
386e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    /* package */ CallerInfo markAsEmergency(Context context) {
387e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        phoneNumber = context.getString(
388e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania            com.android.internal.R.string.emergency_call_dialog_number_for_display);
389e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        photoResource = com.android.internal.R.drawable.picture_emergency;
390e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        mIsEmergency = true;
391e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania        return this;
392e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania    }
393e22415817febc8d3229d1774f3b0dfda0fda8f46Nicolas Catania
39460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
39560d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    /**
39660d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * Mark this CallerInfo as a voicemail call. The voicemail label
39760d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * is obtained from the telephony manager. Caller must hold the
39860d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * READ_PHONE_STATE permission otherwise the phoneNumber will be
39960d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * set to null.
40060d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * @return this instance.
40160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     */
40260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    // TODO: As in the emergency number handling, we end up writing a
40360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    // string in the phone number field.
40460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    /* package */ CallerInfo markAsVoiceMail() {
40560d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        mIsVoiceMail = true;
40660d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
40760d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        try {
40860d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            String voiceMailLabel = TelephonyManager.getDefault().getVoiceMailAlphaTag();
40960d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
41060d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            phoneNumber = voiceMailLabel;
41160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        } catch (SecurityException se) {
41260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            // Should never happen: if this process does not have
41360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            // permission to retrieve VM tag, it should not have
41460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            // permission to retrieve VM number and would not call
41560d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            // this method.
41660d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            // Leave phoneNumber untouched.
41760d45f0f0320801a16db2ad038453c098e98966cNicolas Catania            Log.e(TAG, "Cannot access VoiceMail.", se);
41860d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        }
41960d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // TODO: There is no voicemail picture?
42060d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // FIXME: FIND ANOTHER ICON
42160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        // photoResource = android.R.drawable.badge_voicemail;
42260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania        return this;
42360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    }
42460d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String normalize(String s) {
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (s == null || s.length() > 0) {
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return s;
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return null;
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
43260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania
43360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    /**
43485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * Returns the column index to use to find the "person_id" field in
43585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * the specified cursor, based on the contact URI that was originally
43685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * queried.
43785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     *
43885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * This is a helper function for the getCallerInfo() method that takes
43985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * a Cursor.  Looking up the person_id is nontrivial (compared to all
44085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * the other CallerInfo fields) since the column we need to use
44185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * depends on what query we originally ran.
44285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     *
44385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * Watch out: be sure to not do any database access in this method, since
44485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * it's run from the UI thread (see comments below for more info.)
44585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     *
44685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * @return the columnIndex to use (with cursor.getLong()) to get the
44785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * person_id, or -1 if we couldn't figure out what colum to use.
44885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     *
44985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * TODO: Add a unittest for this method.  (This is a little tricky to
45085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * test, since we'll need a live contacts database to test against,
45185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * preloaded with at least some phone numbers and SIP addresses.  And
45285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * we'll probably have to hardcode the column indexes we expect, so
45385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * the test might break whenever the contacts schema changes.  But we
45485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * can at least make sure we handle all the URI patterns we claim to,
45585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     * and that the mime types match what we expect...)
45685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown     */
45785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown    private static int getColumnIndexForPersonId(Uri contactRef, Cursor cursor) {
45885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // TODO: This is pretty ugly now, see bug 2269240 for
45985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // more details. The column to use depends upon the type of URL:
46085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // - content://com.android.contacts/data/phones ==> use the "contact_id" column
46185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // - content://com.android.contacts/phone_lookup ==> use the "_ID" column
46285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // - content://com.android.contacts/data ==> use the "contact_id" column
46385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // If it's none of the above, we leave columnIndex=-1 which means
46485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // that the person_id field will be left unset.
46585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        //
46685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // The logic here *used* to be based on the mime type of contactRef
46785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // (for example Phone.CONTENT_ITEM_TYPE would tell us to use the
46885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // RawContacts.CONTACT_ID column).  But looking up the mime type requires
46985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // a call to context.getContentResolver().getType(contactRef), which
47085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // isn't safe to do from the UI thread since it can cause an ANR if
47185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // the contacts provider is slow or blocked (like during a sync.)
47285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        //
47385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // So instead, figure out the column to use for person_id by just
47485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // looking at the URI itself.
47585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown
47685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        if (VDBG) Log.v(TAG, "- getColumnIndexForPersonId: contactRef URI = '"
47785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                        + contactRef + "'...");
47885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // Warning: Do not enable the following logging (due to ANR risk.)
47985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        // if (VDBG) Log.v(TAG, "- MIME type: "
48085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        //                 + context.getContentResolver().getType(contactRef));
48185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown
48285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        String url = contactRef.toString();
48385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        String columnName = null;
48485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        if (url.startsWith("content://com.android.contacts/data/phones")) {
48585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // Direct lookup in the Phone table.
48685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // MIME type: Phone.CONTENT_ITEM_TYPE (= "vnd.android.cursor.item/phone_v2")
48785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            if (VDBG) Log.v(TAG, "'data/phones' URI; using RawContacts.CONTACT_ID");
48885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            columnName = RawContacts.CONTACT_ID;
48985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        } else if (url.startsWith("content://com.android.contacts/data")) {
49085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // Direct lookup in the Data table.
49185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // MIME type: Data.CONTENT_TYPE (= "vnd.android.cursor.dir/data")
49285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            if (VDBG) Log.v(TAG, "'data' URI; using Data.CONTACT_ID");
49385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // (Note Data.CONTACT_ID and RawContacts.CONTACT_ID are equivalent.)
49485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            columnName = Data.CONTACT_ID;
49585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        } else if (url.startsWith("content://com.android.contacts/phone_lookup")) {
49685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // Lookup in the PhoneLookup table, which provides "fuzzy matching"
49785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // for phone numbers.
49885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            // MIME type: PhoneLookup.CONTENT_TYPE (= "vnd.android.cursor.dir/phone_lookup")
49985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            if (VDBG) Log.v(TAG, "'phone_lookup' URI; using PhoneLookup._ID");
50085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            columnName = PhoneLookup._ID;
50185e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        } else {
50285e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown            Log.w(TAG, "Unexpected prefix for contactRef '" + url + "'");
50385e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        }
50485e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        int columnIndex = (columnName != null) ? cursor.getColumnIndex(columnName) : -1;
50585e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        if (VDBG) Log.v(TAG, "==> Using column '" + columnName
50685e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown                        + "' (columnIndex = " + columnIndex + ") for person_id lookup...");
50785e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown        return columnIndex;
50885e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown    }
50985e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown
51085e0ff8f3d6e66b0d943851f478863c7afa71e16David Brown    /**
51194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * Updates this CallerInfo's geoDescription field, based on the raw
51294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * phone number in the phoneNumber field.
51394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     *
51494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * (Note that the various getCallerInfo() methods do *not* set the
51594202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * geoDescription automatically; you need to call this method
51694202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * explicitly to get it.)
51794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     *
51894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * @param context the context used to look up the current locale / country
51994202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * @param fallbackNumber if this CallerInfo's phoneNumber field is empty,
52094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     *        this specifies a fallback number to use instead.
52194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     */
52294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    public void updateGeoDescription(Context context, String fallbackNumber) {
52394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        String number = TextUtils.isEmpty(phoneNumber) ? fallbackNumber : phoneNumber;
52494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        geoDescription = getGeoDescription(context, number);
52594202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    }
52694202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
52794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    /**
52894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     * @return a geographical description string for the specified number.
529e713576292fc72086de47066981b86ad2f27ab0fShaopeng Jia     * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
53094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown     */
53194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    private static String getGeoDescription(Context context, String number) {
53294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        if (VDBG) Log.v(TAG, "getGeoDescription('" + number + "')...");
53394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
53494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        if (TextUtils.isEmpty(number)) {
53594202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            return null;
53694202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        }
53794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
53894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        PhoneNumberUtil util = PhoneNumberUtil.getInstance();
53994202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance();
54094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
54194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        Locale locale = context.getResources().getConfiguration().locale;
5429683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia        String countryIso = getCurrentCountryIso(context, locale);
54394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        PhoneNumber pn = null;
54494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        try {
545cec25c4e8afdc56451f7405f8605c1d67433e2ffDavid Brown            if (VDBG) Log.v(TAG, "parsing '" + number
546cec25c4e8afdc56451f7405f8605c1d67433e2ffDavid Brown                            + "' for countryIso '" + countryIso + "'...");
54794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            pn = util.parse(number, countryIso);
54894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            if (VDBG) Log.v(TAG, "- parsed number: " + pn);
54994202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        } catch (NumberParseException e) {
55094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            Log.w(TAG, "getGeoDescription: NumberParseException for incoming number '" + number + "'");
55194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        }
55294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
55394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        if (pn != null) {
55494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            String description = geocoder.getDescriptionForNumber(pn, locale);
55594202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            if (VDBG) Log.v(TAG, "- got description: '" + description + "'");
55694202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            return description;
55794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        } else {
55894202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown            return null;
55994202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown        }
56094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    }
56194202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown
56294202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown    /**
5639683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia     * @return The ISO 3166-1 two letters country code of the country the user
5649683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia     *         is in.
5659683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia     */
5669683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia    private static String getCurrentCountryIso(Context context, Locale locale) {
5679683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      String countryIso;
5689683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      CountryDetector detector = (CountryDetector) context.getSystemService(
5699683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia          Context.COUNTRY_DETECTOR);
5709683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      if (detector != null) {
5719683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia        countryIso = detector.detectCountry().getCountryIso();
5729683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      } else {
5739683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia        countryIso = locale.getCountry();
5749683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia        Log.w(TAG, "No CountryDetector; falling back to countryIso based on locale: "
5759683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia              + countryIso);
5769683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      }
5779683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia      return countryIso;
5789683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia    }
5799683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia
5809683f990a282776ac8a588a9d5e1a73b61f43dcfShaopeng Jia    /**
58160d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     * @return a string debug representation of this instance.
58260d45f0f0320801a16db2ad038453c098e98966cNicolas Catania     */
58360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    public String toString() {
58404639ba0a939988d00131e61458807dac650f9c3David Brown        // Warning: never check in this file with VERBOSE_DEBUG = true
58504639ba0a939988d00131e61458807dac650f9c3David Brown        // because that will result in PII in the system log.
58604639ba0a939988d00131e61458807dac650f9c3David Brown        final boolean VERBOSE_DEBUG = false;
58704639ba0a939988d00131e61458807dac650f9c3David Brown
58804639ba0a939988d00131e61458807dac650f9c3David Brown        if (VERBOSE_DEBUG) {
58904639ba0a939988d00131e61458807dac650f9c3David Brown            return new StringBuilder(384)
59094202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    .append(super.toString() + " { ")
59104639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nname: " + name)
59204639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nphoneNumber: " + phoneNumber)
59394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    .append("\nnormalizedNumber: " + normalizedNumber)
59494202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    .append("\ngeoDescription: " + geoDescription)
59504639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncnapName: " + cnapName)
59604639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nnumberPresentation: " + numberPresentation)
59704639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nnamePresentation: " + namePresentation)
59804639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncontactExits: " + contactExists)
59904639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nphoneLabel: " + phoneLabel)
60004639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nnumberType: " + numberType)
60104639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nnumberLabel: " + numberLabel)
60204639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nphotoResource: " + photoResource)
60304639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nperson_id: " + person_id)
60404639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nneedUpdate: " + needUpdate)
60504639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncontactRefUri: " + contactRefUri)
60604639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncontactRingtoneUri: " + contactRefUri)
60704639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nshouldSendToVoicemail: " + shouldSendToVoicemail)
60804639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncachedPhoto: " + cachedPhoto)
60904639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nisCachedPhotoCurrent: " + isCachedPhotoCurrent)
61004639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nemergency: " + mIsEmergency)
61104639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\nvoicemail " + mIsVoiceMail)
61204639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("\ncontactExists " + contactExists)
61394202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    .append(" }")
61404639ba0a939988d00131e61458807dac650f9c3David Brown                    .toString();
61504639ba0a939988d00131e61458807dac650f9c3David Brown        } else {
61604639ba0a939988d00131e61458807dac650f9c3David Brown            return new StringBuilder(128)
61794202fe1217b9f63e1f5c314379a9f0021e96ea8David Brown                    .append(super.toString() + " { ")
61804639ba0a939988d00131e61458807dac650f9c3David Brown                    .append("name " + ((name == null) ? "null" : "non-null"))
61904639ba0a939988d00131e61458807dac650f9c3David Brown                    .append(", phoneNumber " + ((phoneNumber == null) ? "null" : "non-null"))
62004639ba0a939988d00131e61458807dac650f9c3David Brown                    .append(" }")
62104639ba0a939988d00131e61458807dac650f9c3David Brown                    .toString();
62204639ba0a939988d00131e61458807dac650f9c3David Brown        }
62360d45f0f0320801a16db2ad038453c098e98966cNicolas Catania    }
6242563a3ac05dd3cf8a07203ae682c243f2e793137Wink Saville}
625