通话信息查询分析 --- 之三

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的处理都是异步的,另外开启一个线程,完成之后进行回调,最后显示在界面上。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值