/** * getCallerInfo given a phone number and subscription, look up in the call-log database for the * matching caller id info. * * @param context the context used to get the ContentResolver * @param number the phone number used to lookup caller id * @param subId the subscription for checking for if voice mail number or not * @return the CallerInfo which contains the caller id for the given number. The returned * CallerInfo is null if no number is supplied. If a matching number is not found, then a * generic caller info is returned, with all relevant fields empty or null. */ public static CallerInfo getCallerInfo(Context context, String number, int subId) { if (TextUtils.isEmpty(number)) { return null; } // Change the callerInfo number ONLY if it is an emergency number // or if it is the voicemail number. If it is either, take a // shortcut and skip the query. if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) { return new CallerInfo().markAsEmergency(context); } else if (PhoneNumberUtils.isVoiceMailNumber(subId, number)) { return new CallerInfo().markAsVoiceMail(); } Uri contactUri = Uri.withAppendedPath(PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI, Uri.encode(number)); CallerInfo info = getCallerInfo(context, contactUri); info = doSecondaryLookupIfNecessary(context, number, info); // if no query results were returned with a viable number, // fill in the original number value we used to query with. if (TextUtils.isEmpty(info.phoneNumber)) { info.phoneNumber = number; } return info; }
/** * Overrides onQueryComplete from AsyncQueryHandler. * * <p>This method takes into account the state of this class; we construct the CallerInfo object * only once for each set of listeners. When the query thread has done its work and calls this * method, we inform the remaining listeners in the queue, until we're out of listeners. Once we * get the message indicating that we should expect no new listeners for this CallerInfo object, * we release the AsyncCursorInfo back into the pool. */ @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { if (DBG) log("query complete for token: " + token); // get the cookie and notify the listener. CookieWrapper cw = (CookieWrapper) cookie; if (cw == null) { // Normally, this should never be the case for calls originating // from within this code. // However, if there is any code that calls this method, we should // check the parameters to make sure they're viable. if (DBG) log("Cookie is null, ignoring onQueryComplete() request."); return; } if (cw.event == EVENT_END_OF_QUEUE) { release(); return; } // check the token and if needed, create the callerinfo object. if (mCallerInfo == null) { if ((mQueryContext == null) || (mQueryUri == null)) { throw new QueryPoolException( "Bad context or query uri, or CallerInfoAsyncQuery already released."); } // adjust the callerInfo data as needed, and only if it was set from the // initial query request. // Change the callerInfo number ONLY if it is an emergency number or the // voicemail number, and adjust other data (including photoResource) // accordingly. if (cw.event == EVENT_EMERGENCY_NUMBER) { // Note we're setting the phone number here (refer to javadoc // comments at the top of CallerInfo class). mCallerInfo = new CallerInfo().markAsEmergency(mQueryContext); } else if (cw.event == EVENT_VOICEMAIL_NUMBER) { mCallerInfo = new CallerInfo().markAsVoiceMail(); } else { mCallerInfo = CallerInfo.getCallerInfo(mQueryContext, mQueryUri, cursor); // Use the number entered by the user for display. if (!TextUtils.isEmpty(cw.number)) { mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number); } } if (DBG) log("constructing CallerInfo object for token: " + token); // notify that we can clean up the queue after this. CookieWrapper endMarker = new CookieWrapper(); endMarker.event = EVENT_END_OF_QUEUE; startQuery(token, endMarker, null, null, null, null, null); } // notify the listener that the query is complete. if (cw.listener != null) { if (DBG) log( "notifying listener: " + cw.listener.getClass().toString() + " for token: " + token + mCallerInfo); cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo); } }
/** * getCallerInfo given a Cursor. * * @param context the context used to retrieve string constants * @param contactRef the URI to attach to this CallerInfo object * @param cursor the first object in the cursor is used to build the CallerInfo object. * @return the CallerInfo which contains the caller id for the given number. The returned * CallerInfo is null if no number is supplied. */ public static CallerInfo getCallerInfo(Context context, Uri contactRef, Cursor cursor) { CallerInfo info = new CallerInfo(); info.photoResource = 0; info.phoneLabel = null; info.numberType = 0; info.numberLabel = null; info.cachedPhoto = null; info.isCachedPhotoCurrent = false; info.contactExists = false; if (VDBG) Rlog.v(TAG, "getCallerInfo() based on cursor..."); if (cursor != null) { if (cursor.moveToFirst()) { // TODO: photo_id is always available but not taken // care of here. Maybe we should store it in the // CallerInfo object as well. int columnIndex; // Look for the name columnIndex = cursor.getColumnIndex(PhoneLookup.DISPLAY_NAME); if (columnIndex != -1) { info.name = cursor.getString(columnIndex); } // Look for the number columnIndex = cursor.getColumnIndex(PhoneLookup.NUMBER); if (columnIndex != -1) { info.phoneNumber = cursor.getString(columnIndex); } // Look for the normalized number columnIndex = cursor.getColumnIndex(PhoneLookup.NORMALIZED_NUMBER); if (columnIndex != -1) { info.normalizedNumber = cursor.getString(columnIndex); } // Look for the label/type combo columnIndex = cursor.getColumnIndex(PhoneLookup.LABEL); if (columnIndex != -1) { int typeColumnIndex = cursor.getColumnIndex(PhoneLookup.TYPE); if (typeColumnIndex != -1) { info.numberType = cursor.getInt(typeColumnIndex); info.numberLabel = cursor.getString(columnIndex); info.phoneLabel = Phone.getDisplayLabel(context, info.numberType, info.numberLabel).toString(); } } // Look for the person_id. columnIndex = getColumnIndexForPersonId(contactRef, cursor); if (columnIndex != -1) { final long contactId = cursor.getLong(columnIndex); if (contactId != 0 && !Contacts.isEnterpriseContactId(contactId)) { info.contactIdOrZero = contactId; if (VDBG) { Rlog.v(TAG, "==> got info.contactIdOrZero: " + info.contactIdOrZero); } } } else { // No valid columnIndex, so we can't look up person_id. Rlog.w(TAG, "Couldn't find contact_id column for " + contactRef); // Watch out: this means that anything that depends on // person_id will be broken (like contact photo lookups in // the in-call UI, for example.) } // Contact lookupKey columnIndex = cursor.getColumnIndex(PhoneLookup.LOOKUP_KEY); if (columnIndex != -1) { info.lookupKey = cursor.getString(columnIndex); } // Display photo URI. columnIndex = cursor.getColumnIndex(PhoneLookup.PHOTO_URI); if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { info.contactDisplayPhotoUri = Uri.parse(cursor.getString(columnIndex)); } else { info.contactDisplayPhotoUri = null; } // look for the custom ringtone, create from the string stored // in the database. columnIndex = cursor.getColumnIndex(PhoneLookup.CUSTOM_RINGTONE); if ((columnIndex != -1) && (cursor.getString(columnIndex) != null)) { info.contactRingtoneUri = Uri.parse(cursor.getString(columnIndex)); } else { info.contactRingtoneUri = null; } // look for the send to voicemail flag, set it to true only // under certain circumstances. columnIndex = cursor.getColumnIndex(PhoneLookup.SEND_TO_VOICEMAIL); info.shouldSendToVoicemail = (columnIndex != -1) && ((cursor.getInt(columnIndex)) == 1); info.contactExists = true; } cursor.close(); cursor = null; } info.needUpdate = false; info.name = normalize(info.name); info.contactRefUri = contactRef; return info; }