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