CallerInfo.java revision 2563a3ac05dd3cf8a07203ae682c243f2e793137
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.telephony;
18
19import android.content.Context;
20import android.database.Cursor;
21import android.graphics.drawable.Drawable;
22import android.net.Uri;
23import android.provider.Contacts;
24import android.provider.Contacts.People;
25import android.provider.Contacts.Phones;
26import android.text.TextUtils;
27import android.telephony.TelephonyManager;
28import android.telephony.PhoneNumberUtils;
29import android.util.Config;
30import android.util.Log;
31
32/**
33 * Looks up caller information for the given phone number.
34 *
35 * {@hide}
36 */
37public class CallerInfo {
38    private static final String TAG = "CallerInfo";
39
40    public static final String UNKNOWN_NUMBER = "-1";
41    public static final String PRIVATE_NUMBER = "-2";
42    public static final String PAYPHONE_NUMBER = "-3";
43
44    /**
45     * Please note that, any one of these member variables can be null,
46     * and any accesses to them should be prepared to handle such a case.
47     *
48     * Also, it is implied that phoneNumber is more often populated than
49     * name is, (think of calls being dialed/received using numbers where
50     * names are not known to the device), so phoneNumber should serve as
51     * a dependable fallback when name is unavailable.
52     *
53     * One other detail here is that this CallerInfo object reflects
54     * information found on a connection, it is an OUTPUT that serves
55     * mainly to display information to the user.  In no way is this object
56     * used as input to make a connection, so we can choose to display
57     * whatever human-readable text makes sense to the user for a
58     * connection.  This is especially relevant for the phone number field,
59     * since it is the one field that is most likely exposed to the user.
60     *
61     * As an example:
62     *   1. User dials "911"
63     *   2. Device recognizes that this is an emergency number
64     *   3. We use the "Emergency Number" string instead of "911" in the
65     *     phoneNumber field.
66     *
67     * What we're really doing here is treating phoneNumber as an essential
68     * field here, NOT name.  We're NOT always guaranteed to have a name
69     * for a connection, but the number should be displayable.
70     */
71    public String name;
72    public String phoneNumber;
73
74    public String cnapName;
75    public int numberPresentation;
76    public int namePresentation;
77    public boolean contactExists;
78
79    public String phoneLabel;
80    /* Split up the phoneLabel into number type and label name */
81    public int    numberType;
82    public String numberLabel;
83
84    public int photoResource;
85    public long person_id;
86    public boolean needUpdate;
87    public Uri contactRefUri;
88
89    // fields to hold individual contact preference data,
90    // including the send to voicemail flag and the ringtone
91    // uri reference.
92    public Uri contactRingtoneUri;
93    public boolean shouldSendToVoicemail;
94
95    /**
96     * Drawable representing the caller image.  This is essentially
97     * a cache for the image data tied into the connection /
98     * callerinfo object.  The isCachedPhotoCurrent flag indicates
99     * if the image data needs to be reloaded.
100     */
101    public Drawable cachedPhoto;
102    public boolean isCachedPhotoCurrent;
103
104    // Don't keep checking VM if it's going to throw an exception for this proc.
105    private static boolean sSkipVmCheck = false;
106
107    public CallerInfo() {
108    }
109
110    /**
111     * getCallerInfo given a Cursor.
112     * @param context the context used to retrieve string constants
113     * @param contactRef the URI to attach to this CallerInfo object
114     * @param cursor the first object in the cursor is used to build the CallerInfo object.
115     * @return the CallerInfo which contains the caller id for the given
116     * number. The returned CallerInfo is null if no number is supplied.
117     */
118    public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) {
119
120        CallerInfo info = new CallerInfo();
121        info.photoResource = 0;
122        info.phoneLabel = null;
123        info.numberType = 0;
124        info.numberLabel = null;
125        info.cachedPhoto = null;
126        info.isCachedPhotoCurrent = false;
127        info.contactExists = false;
128
129        if (Config.LOGV) Log.v(TAG, "construct callerInfo from cursor");
130
131        if (cursor != null) {
132            if (cursor.moveToFirst()) {
133
134                int columnIndex;
135
136                // Look for the name
137                columnIndex = cursor.getColumnIndex(People.NAME);
138                if (columnIndex != -1) {
139                    info.name = cursor.getString(columnIndex);
140                }
141
142                // Look for the number
143                columnIndex = cursor.getColumnIndex(Phones.NUMBER);
144                if (columnIndex != -1) {
145                    info.phoneNumber = cursor.getString(columnIndex);
146                }
147
148                // Look for the label/type combo
149                columnIndex = cursor.getColumnIndex(Phones.LABEL);
150                if (columnIndex != -1) {
151                    int typeColumnIndex = cursor.getColumnIndex(Phones.TYPE);
152                    if (typeColumnIndex != -1) {
153                        info.numberType = cursor.getInt(typeColumnIndex);
154                        info.numberLabel = cursor.getString(columnIndex);
155                        info.phoneLabel = Contacts.Phones.getDisplayLabel(context,
156                                info.numberType, info.numberLabel)
157                                .toString();
158                    }
159                }
160
161                // Look for the person ID
162                columnIndex = cursor.getColumnIndex(Phones.PERSON_ID);
163                if (columnIndex != -1) {
164                    info.person_id = cursor.getLong(columnIndex);
165                } else {
166                    columnIndex = cursor.getColumnIndex(People._ID);
167                    if (columnIndex != -1) {
168                        info.person_id = cursor.getLong(columnIndex);
169                    }
170                }
171
172                // look for the custom ringtone, create from the string stored
173                // in the database.
174                columnIndex = cursor.getColumnIndex(People.CUSTOM_RINGTONE);
175                if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) {
176                    info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex));
177                } else {
178                    info.contactRingtoneUri = null;
179                }
180
181                // look for the send to voicemail flag, set it to true only
182                // under certain circumstances.
183                columnIndex = cursor.getColumnIndex(People.SEND_TO_VOICEMAIL);
184                info.shouldSendToVoicemail = (columnIndex != -1) &&
185                        ((cursor.getInt(columnIndex)) == 1);
186                info.contactExists = true;
187            }
188            cursor.close();
189        }
190
191        info.needUpdate = false;
192        info.name = normalize(info.name);
193        info.contactRefUri = contactRef;
194
195        return info;
196    }
197
198    /**
199     * getCallerInfo given a URI, look up in the call-log database
200     * for the uri unique key.
201     * @param context the context used to get the ContentResolver
202     * @param contactRef the URI used to lookup caller id
203     * @return the CallerInfo which contains the caller id for the given
204     * number. The returned CallerInfo is null if no number is supplied.
205     */
206    public static CallerInfo getCallerInfo(Context context, Uri contactRef) {
207
208        return getCallerInfo(context, contactRef,
209                context.getContentResolver().query(contactRef, null, null, null, null));
210    }
211
212    /**
213     * getCallerInfo given a phone number, look up in the call-log database
214     * for the matching caller id info.
215     * @param context the context used to get the ContentResolver
216     * @param number the phone number used to lookup caller id
217     * @return the CallerInfo which contains the caller id for the given
218     * number. The returned CallerInfo is null if no number is supplied. If
219     * a matching number is not found, then a generic caller info is returned,
220     * with all relevant fields empty or null.
221     */
222    public static CallerInfo getCallerInfo(Context context, String number) {
223        if (TextUtils.isEmpty(number)) {
224            return null;
225        } else {
226            // Change the callerInfo number ONLY if it is an emergency number
227            // or if it is the voicemail number.  If it is either, take a
228            // shortcut and skip the query.
229            if (PhoneNumberUtils.isEmergencyNumber(number)) {
230                CallerInfo ci = new CallerInfo();
231
232                // Note we're setting the phone number here (refer to javadoc
233                // comments at the top of CallerInfo class).
234                ci.phoneNumber = context.getString(
235                        com.android.internal.R.string.emergency_call_dialog_number_for_display);
236                return ci;
237            } else {
238                try {
239                    if (!sSkipVmCheck && PhoneNumberUtils.compare(number,
240                                TelephonyManager.getDefault().getVoiceMailNumber())) {
241                        CallerInfo ci = new CallerInfo();
242
243                        // Note we're setting the phone number here (refer to javadoc
244                        // comments at the top of CallerInfo class).
245                        ci.phoneNumber = TelephonyManager.getDefault().getVoiceMailAlphaTag();
246                        // TODO: FIND ANOTHER ICON
247                        //info.photoResource = android.R.drawable.badge_voicemail;
248                        return ci;
249                    }
250                } catch (SecurityException ex) {
251                    // Don't crash if this process doesn't have permission to
252                    // retrieve VM number.  It's still allowed to look up caller info.
253                    // But don't try it again.
254                    sSkipVmCheck = true;
255                }
256            }
257        }
258
259        Uri contactUri = Uri.withAppendedPath(Contacts.Phones.CONTENT_FILTER_URL,
260                                              Uri.encode(number));
261
262        CallerInfo info = getCallerInfo(context, contactUri);
263
264        // if no query results were returned with a viable number,
265        // fill in the original number value we used to query with.
266        if (TextUtils.isEmpty(info.phoneNumber)) {
267            info.phoneNumber = number;
268        }
269
270        return info;
271    }
272
273    /**
274     * getCallerId: a convenience method to get the caller id for a given
275     * number.
276     *
277     * @param context the context used to get the ContentResolver.
278     * @param number a phone number.
279     * @return if the number belongs to a contact, the contact's name is
280     * returned; otherwise, the number itself is returned.
281     *
282     * TODO NOTE: This MAY need to refer to the Asynchronous Query API
283     * [startQuery()], instead of getCallerInfo, but since it looks like
284     * it is only being used by the provider calls in the messaging app:
285     *   1. android.provider.Telephony.Mms.getDisplayAddress()
286     *   2. android.provider.Telephony.Sms.getDisplayAddress()
287     * We may not need to make the change.
288     */
289    public static String getCallerId(Context context, String number) {
290        CallerInfo info = getCallerInfo(context, number);
291        String callerID = null;
292
293        if (info != null) {
294            String name = info.name;
295
296            if (!TextUtils.isEmpty(name)) {
297                callerID = name;
298            } else {
299                callerID = number;
300            }
301        }
302
303        return callerID;
304    }
305
306    private static String normalize(String s) {
307        if (s == null || s.length() > 0) {
308            return s;
309        } else {
310            return null;
311        }
312    }
313}
314