1,buildEntry 方法分析
ContactInfoCache的buildEntry方法调用流程图如下,
buildEntry方法主要逻辑如下,
1,首先构造ContactCacheEntry对象,然后调用populateCacheEntry方法利用CallerInfo对象为ContactCacheEntry赋值,
final ContactCacheEntry cce = new ContactCacheEntry();
populateCacheEntry(context, info, cce, presentation, isIncoming);
2,保存photo等信息,
if (info.photoResource != 0) {
photo = context.getResources().getDrawable(info.photoResource);//获取查询到的photo
} else if (info.isCachedPhotoCurrent) {
if (info.cachedPhoto != null) {
photo = info.cachedPhoto;
} else {
photo = getDefaultContactPhotoDrawable(); //获取默认的photo
}
} else if (info.contactDisplayPhotoUri == null) {
photo = getDefaultContactPhotoDrawable();//获取默认的photo
} else {
cce.displayPhotoUri = info.contactDisplayPhotoUri;
}
ContactCacheEntry是ContactInfoCache内部的一个简单类,比CallerInfo类还简单。如下,
public static class ContactCacheEntry {
public String name; //姓名
public String number; //号码
public String location; //归属地
public String label; //标签
public Drawable photo; //图像
public boolean isSipCall; //是否是Sip通话
/** This will be used for the "view" notification. */
public Uri contactUri;
/** Either a display photo or a thumbnail URI. */
public Uri displayPhotoUri;
public Uri lookupUri; // Sent to NotificationMananger
public String lookupKey;
除此之外,还有一个显示所有信息的toString方法。
populateCacheEntry方法的主要是为了保存号码,姓名,归属地等信息,
1,处理SIP拨号号码,
if (!TextUtils.isEmpty(number)) {
isSipCall = PhoneNumberHelper.isUriNumber(number);
if (number.startsWith("sip:")) {
number = number.substring(4);
}
}
2,处理特殊的显示信息,
displayName = getPresentationString(context, presentation, info.callSubject);
getPresentationString方法如下,
String name = context.getString(R.string.unknown); //未知号码
if (!TextUtils.isEmpty(customLabel) &&
((presentation == TelecomManager.PRESENTATION_UNKNOWN) ||
(presentation == TelecomManager.PRESENTATION_RESTRICTED))) {
name = customLabel;
return name;
} else {
if (presentation == TelecomManager.PRESENTATION_RESTRICTED) {
name = context.getString(R.string.private_num); //私人号码
} else if (presentation == TelecomManager.PRESENTATION_PAYPHONE) {
name = context.getString(R.string.payphone); //公用电话
}
}
return name;
在特殊情况下会显示特殊的displayName, 例如号码隐藏服务,即当用户开通该业务后,网络侧返回数据中不会包含该用户的号码信息。
比如一名用户开启了该服务,呼叫该用户或者该用户来电时,当该用户接通来电后,主叫设备上不会显示对方的号码或者联系人信息,取而代之的是Unknown(未知号码)。
3,处理显示名称等信息,例如, 如果当前通话的号码并未存储到用户的联系人列表中,将displayNumber设置为对应的号码,后面显示的时候会判断,如果displayName为空的话,就显示displayNumber,
displayNumber = number;
if (isIncoming) {
displayLocation = info.geoDescription; //如果是来电,则显示号码归属地相关信息
Log.d(TAG, "Geodescrption: " + info.geoDescription);
}
4,最后保存信息,
cce.name = displayName;
cce.number = displayNumber;
cce.location = displayLocation;
cce.label = label;
cce.isSipCall = isSipCall;
2,startObtainPhotoAsync方法分析
ContactsAsyncHelper的startObtainPhotoAsync方法主要是对photo的大小进行异步处理,
处理完成之后会调用ContactInfoCache的onImageLoadComplete方法。
ContactInfoCache实现了ContactsAsyncHelper的内部接口OnImageLoadCompleteListener的onImageLoadComplete方法。
ContactsAsyncHelper实现逻辑和AsyncQueryHandler完全类似,只是多了一个回调的接口,并且AsyncQueryHandler 是专门用来查询联系人的,可以认为ContactsAsyncHelper就是AsyncQueryHandler的翻版。
主要看ContactsAsyncHelper 的内部类WorkerHandler的handleMessage方法,
首先根据displayPhotoUri 信息构造Bitmap类型的Drawable,
然后调用getPhotoIconWhenAppropriate方法进行缩放,
args.photo = Drawable.createFromStream(inputStream, args.displayPhotoUri.toString());
args.photoIcon = getPhotoIconWhenAppropriate(args.context, args.photo);
getPhotoIconWhenAppropriate方法如下,
//获取系统要求的显示尺寸
int iconSize = context.getResources().getDimensionPixelSize(R.dimen.notification_icon_size);
Bitmap orgBitmap = ((BitmapDrawable) photo).getBitmap();
//获取Bitmap 的宽和高
int orgWidth = orgBitmap.getWidth();
int orgHeight = orgBitmap.getHeight();
int longerEdge = orgWidth > orgHeight ? orgWidth : orgHeight;
// We want downscaled one only when the original icon is too big.
if (longerEdge > iconSize) { // 如果大于就按照比例进行缩放
float ratio = ((float) longerEdge) / iconSize;
int newWidth = (int) (orgWidth / ratio);
int newHeight = (int) (orgHeight / ratio);
•••
return Bitmap.createScaledBitmap(orgBitmap, newWidth, newHeight, true);
} else {
return orgBitmap;
}
最后处理完成之后, mResultHandler变量的handleMessage方法如下,
args.listener.onImageLoadComplete(msg.what, args.photo, args.photoIcon, args.cookie);
回调监听器的onImageLoadComplete方法,也就是ContactInfoCache的onImageLoadComplete方法,该方法如下,
1,首先将photo 或者photoIcon 信息保存在ContactCacheEntry的photo变量中,
if (photo != null) {
Log.v(this, "direct drawable: ", photo);
entry.photo = photo;
} else if (photoIcon != null) {
Log.v(this, "photo icon: ", photoIcon);
entry.photo = new BitmapDrawable(mContext.getResources(), photoIcon);
} else {
Log.v(this, "unknown photo");
entry.photo = null;
}
2,调用sendImageNotifications方法将图片处理完成的消息发送出去,
sendImageNotifications(callId, entry);
clearCallbacks(callId); //清空监听器
sendImageNotifications方法如下,
final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
if (callBacks != null && entry.photo != null) {
for (ContactInfoCacheCallback callBack : callBacks) {
callBack.onImageLoadComplete(callId, entry);
}
}
当然,在此就是调用CallCardPresenter的内部类ContactLookupCallback的onImageLoadComplete方法。sendImageNotifications的调用过程和sendInfoNotifications方法的调用过程完全相同,都在下个小节中论述。
3,sendInfoNotifications方法分析
ContactInfoCache的sendInfoNotifications方法和sendImageNotifications方法调用流程图如下,
sendInfoNotifications方法如下,
final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
if (callBacks != null) {
for (ContactInfoCacheCallback callBack : callBacks) {
callBack.onContactInfoComplete(callId, entry);
}
}
调用CallCardPresenter的内部类ContactLookupCallback的onContactInfoComplete方法。
中间的调用过程在此就不详述了,基本没什么难点。
最后CallCardFragment的setPrimary和setPrimaryImage方法也是将信息显示在对应的View上。
小结:
第一阶段:查询
1,CallList.java onStateChange()
当Call的状态发生改变时,判断Call的状态是否为PENDING_OUTGOING,如果是就利用Call的信息调用maybeStartSearch() 开始查询。
2,ContactInfoCache.java, CallerInfoUtils.java CallerInfoAsyncQuery.java 完成查询。
查询完之后将信息打包到CallerInfo.java 中,然后调用ContactInfoCache.java 的onQueryComplete() 函数在通话界面显示查询的信息。
第二阶段:显示
1, ContactInfoCache.java 调用buildEntry() 根据CallerInfo 得到查询到的名字
(displayName)、号码()、归属地()、是否是Sip电话(isSipCall)等信息,然后将这些信息打包到ContactCacheEntry(ContactInfoCache.java的内部类)实例中。最后继续调用CallCardFragment.java 的setPrimary() 函数显示界面
2, CallCardFragment.java
依次调用setPrimaryName( ),setPrimaryPhoneNumber( );setPrimaryLabel( );
showCallTypeLabel()完成姓名,号码,标签,拨号类型的显示。
通话信息的查询和Photo的处理都是异步的,另外开启一个线程,完成之后进行回调,最后显示在界面上。