通话记录分析
来源:互联网 发布:mac配置qq企业邮箱 编辑:程序博客网 时间:2024/05/23 23:22
1,通话记录
1.1 初始化
在Dialer中,通话记录信息都是通过CallLogActivity 显示,实际上,真正完成的是CallLogFragment 。CallLogActivity的内部类
ViewPagerAdapter的getItem方法如下,
public Fragment getItem(int position) { switch (getRtlPosition(position)) { case TAB_INDEX_ALL: return new CallLogFragment(CallLogQueryHandler.CALL_TYPE_ALL); case TAB_INDEX_MISSED: return new CallLogFragment(Calls.MISSED_TYPE); } throw new IllegalStateException("No fragment at position " + position);}
查询的数据库:contacts2.calls
通话记录没有搜索,在onCreateView函数里根据不同的参数直接查询。
查询的时间顺序是有近到远。
CallLogFragment的构造方法如下,
public CallLogFragment(int filterType, int logLimit, long dateLimit) { mCallTypeFilter = filterType;//查询通话记录的类型 mLogLimit = logLimit; mDateLimit = dateLimit;}
通话记录主要包括以下类型:
所有通话,未接来电,所有外拨电话,所有来电,黑名单来电。
CallLogQueryHandler对应的定义如下,
private static final int INCOMING_IMS_TYPE = 5;private static final int OUTGOING_IMS_TYPE = 6;private static final int MISSED_IMS_TYPE = 7;•••
CallLogFragment的onCreate方法主要逻辑如下,
final Activity activity = getActivity();//获取所在的Activity对象//获取进程的ContentResolver对象final ContentResolver resolver = activity.getContentResolver();String currentCountryIso = GeoUtil.getCurrentCountryIso(activity);//构造CallLogQueryHandler对象mCallLogQueryHandler = new CallLogQueryHandler(activity, resolver, this, mLogLimit);//锁屏管理mKeyguardManager = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);//注册通话记录数据库监听resolver.registerContentObserver(CallLog.CONTENT_URI, true, mCallLogObserver);//注册联系人数据库监听resolver.registerContentObserver(ContactsContract.Contacts.CONTENT_URI, true, mContactsObserver);resolver.registerContentObserver(Status.CONTENT_URI, true, mVoicemailStatusObserver);setHasOptionsMenu(true);//设置菜单
CallLogFragment的onCreateView方法主要逻辑如下,
1,获取RecyclerView布局,
mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);mRecyclerView.setHasFixedSize(true);mLayoutManager = new LinearLayoutManager(getActivity());mRecyclerView.setLayoutManager(mLayoutManager);
2,构造RecyclerView的Adapter
mAdapter = ObjectFactory.newCallLogAdapter(getActivity(),this, new ContactInfoHelper(getActivity(), currentCountryIso), mVoicemailPlaybackPresenter, isShowingRecentsTab);mRecyclerView.setAdapter(mAdapter);
3,调用fetchCalls方法开始查询通话记录
fetchCalls();
1.2 查询通话记录
CallLogFragment的fetchCalls调用流程图如下,
fetchCalls方法如下,
public void fetchCalls() { mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);}
CallLogQueryHandler有不同参数的fetchCalls方法,最后的fetchCalls方法主要逻辑如下,
1,构造查询语句,
StringBuilder where = new StringBuilder();List<String> selectionArgs = Lists.newArrayList();// Ignore voicemails marked as deletedwhere.append(Voicemails.DELETED);where.append(" = 0");if (newOnly) { where.append(" AND "); where.append(Calls.NEW); where.append(" = 1");}•••
2,根据通话记录查询类型构造查询参数,
if (callType > CALL_TYPE_ALL) { if (where.length() > 0) { where.append(" AND "); } if ((callType == Calls.INCOMING_TYPE) || (callType == Calls.OUTGOING_TYPE) || (callType == Calls.MISSED_TYPE)) { where.append(String.format("(%s = ? OR %s = ?)", Calls.TYPE, Calls.TYPE)); } else { // Add a clause to fetch only items of type voicemail. where.append(String.format("(%s = ?)", Calls.TYPE)); } // Add a clause to fetch only items newer than the requested date selectionArgs.add(Integer.toString(callType)); if (callType == Calls.INCOMING_TYPE) { selectionArgs.add(Integer.toString(INCOMING_IMS_TYPE)); } else if (callType == Calls.OUTGOING_TYPE) { selectionArgs.add(Integer.toString(OUTGOING_IMS_TYPE)); } else if (callType == Calls.MISSED_TYPE) { selectionArgs.add(Integer.toString(MISSED_IMS_TYPE)); }•••
3,获取URI
final int limit = (mLogLimit == -1) ? NUM_LOGS_TO_DISPLAY : mLogLimit;final String selection = where.length() > 0 ? where.toString() : null;Uri uri = TelecomUtil.getCallLogUri(mContext).buildUpon() .appendQueryParameter(Calls.LIMIT_PARAM_KEY, Integer.toString(limit)).build();
4,调用startQuery方法进行查询,
startQuery(token, null, uri, CallLogQuery._PROJECTION, selection, selectionArgs.toArray(EMPTY_STRING_ARRAY), Calls.DEFAULT_SORT_ORDER);
父类NoNullCursorAsyncQueryHandler的startQuery方法如下,
public void startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy) {final CookieWithProjection projectionCookie = new CookieWithProjection(cookie, projection); super.startQuery(token, projectionCookie, uri, projection, selection, selectionArgs, orderBy);}
直接调用父类AsyncQueryHandler的startQuery方法进行异步查询, AsyncQueryHandler的原理在此不论述了。
只需要知道的是AsyncQueryHandler查询完成之后会回调onQueryComplete方法。
NoNullCursorAsyncQueryHandler的onQueryComplete方法如下,
protected final void onQueryComplete(int token, Object cookie, Cursor cursor) { CookieWithProjection projectionCookie = (CookieWithProjection) cookie; super.onQueryComplete(token, projectionCookie.originalCookie, cursor); if (cursor == null) { cursor = new EmptyCursor(projectionCookie.projection); } onNotNullableQueryComplete(token, projectionCookie.originalCookie, cursor);}
onNotNullableQueryComplete方法是一个abstract方法,子类CallLogQueryHandler的实现如下,
if (token == QUERY_CALLLOG_TOKEN) { if (updateAdapterData(cursor)) { cursor = null; }•••
如果是普通的通话记录,就调用updateAdapterData方法更新数据。
updateAdapterData方法如下,
private boolean updateAdapterData(Cursor cursor) { final Listener listener = mListener.get(); if (listener != null) { return listener.onCallsFetched(cursor); } return false;}
回调监听器的onCallsFetched方法,当然是在CallLogFragment实现。
监听器Listener是CallLogQueryHandler的内部接口,仅有2个方法,
public interface Listener { /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */ void onVoicemailStatusFetched(Cursor statusCursor); /** * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete. * Returns true if takes ownership of cursor. */ boolean onCallsFetched(Cursor combinedCursor);}
mListener是一个WeakReference组,
private final WeakReference<Listener> mListener;
在CallLogQueryHandler的构造方法中初始化,
public CallLogQueryHandler(Context context, ContentResolver contentResolver, Listener listener, int limit) { super(contentResolver); mContext = context.getApplicationContext(); mListener = new WeakReference<Listener>(listener); mLogLimit = limit;}
在CallLogFragment的onCallsFetched方法会完成通话记录的更新显示。
1.3 更新显示
CallLogFragment的onCallsFetched方法主要逻辑如下,
mAdapter.changeCursor(cursor);
mAdapter 是CallLogAdapter对象, 并且继承于GroupingListAdapter, GroupingListAdapter定义如下,
abstract class GroupingListAdapter extends RecyclerView.Adapter {
其中, GroupingListAdapter实现了RecyclerView.Adapter 的getItemCount方法,
CallLogAdapter实现了RecyclerView.Adapter的onCreateViewHolder/ onBindViewHolder方法。
GroupingListAdapter的changeCursor方法主要逻辑如下,
1,为mCursor变量赋值,
mCursor = cursor;
2,调用findGroups方法对查询到的通话记录分组,
findGroups();
3,更新界面。
notifyDataSetChanged();
notifyDataSetChanged原理在此就不论述了,总之会调用getItemCount/onCreateViewHolder/ onBindViewHolder方法更新界面。
1.3.1 分组
看通话记录界面,可以看到:
1,通话记录分为三类:今天,昨天,更早。如何分类的?
2,相邻的同一号码为一组显示。如何做到的?
查询完之后,会做两件事情,分组(分为今天,昨天以及更早),相邻的相同号码的通话记录分为一组。两件事情在两个不同的类中进行,但是同时进行。
首先在CallLogGroupBuilder类中的addGroups进行分组,分为今天,昨天以及更早。分组的依据是将通话时的时间和当前的时间进行对比。
int currentGroupDayGroup = getDayGroup(firstDate, currentTime);mGroupCreator.setDayGroup(firstRowId, currentGroupDayGroup);
然后调用CallLogAdapter 的setDayGroup 函数将相关信息存储在
mDayGroups(HashMap)中。在bindCallLogListViewHolder函数显示时,在该HashMap中查询当前和前一个Cursor的分组信息,
如果不相同,就显示一个分隔组(昨天或者更早)
int currentGroup = getDayGroupForCall(views.rowId);int previousGroup = getPreviousDayGroup(c);if (currentGroup != previousGroup) { views.dayGroupHeader.setVisibility(View.VISIBLE); views.dayGroupHeader.setText(getGroupDescription(currentGroup));} else { views.dayGroupHeader.setVisibility(View.GONE); }
不仅如此,在addGroups函数中,会比较相邻号码等相关信息是否相同,如果相同就是一个组,如果不同就新建一个组。
然后调用GroupingListAdapter的addGroup函数将这些分组信息保存在64位的mGroupMetadata数组中。其中,高位表示分组的大小,
低位表示分组的起始位置,这两个信息很重要,是显示的基础。
long metadata = ((long)size << 32) | cursorPosition;mGroupMetadata[mGroupCount++] = metadata;
比如:通话记录(3,1,2,2)共8条通话记录,分为4个组,保存的信息为(0,3),(3,1),(4,2),
(6,2)。GroupingListAdapter中的getItemCount()函数根据相关信息,返回的结果为通话记录的组数,而不是单条的通话记录,
这和listview的不一样。这样就回答了上面的两个疑惑。
1.3.2 onCreateViewHolder
CallLogAdapter的onCreateViewHolder方法如下,
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if (viewType == VIEW_TYPE_SHOW_CALL_HISTORY_LIST_ITEM) { return ShowCallHistoryViewHolder.create(mContext, parent); } else if (viewType == VIEW_TYPE_VOICEMAIL_PROMO_CARD) { return createVoicemailPromoCardViewHolder(parent); } return createCallLogEntryViewHolder(parent);}
createCallLogEntryViewHolder的方法如下,
private ViewHolder createCallLogEntryViewHolder(ViewGroup parent) { LayoutInflater inflater = LayoutInflater.from(mContext); View view = inflater.inflate(R.layout.call_log_list_item, parent, false); CallLogListItemViewHolder viewHolder = CallLogListItemViewHolder.create( view, mContext, mExpandCollapseListener, mTelecomCallLogCache, mCallLogListItemHelper, mVoicemailPlaybackPresenter); viewHolder.callLogEntryView.setTag(viewHolder); viewHolder.callLogEntryView.setAccessibilityDelegate(mAccessibilityDelegate); viewHolder.primaryActionView.setOnCreateContextMenuListener(mOnCreateContextMenuListener);viewHolder.primaryActionView.setTag(viewHolder); return viewHolder;}
由此,一条通话记录就对应一个CallLogListItemViewHolder对象。
1.3.3 onBindViewHolder
CallLogAdapter的onBindViewHolder方法会调用bindCallLogListViewHolder方法加载每条通话记录的信息,
bindCallLogListViewHolder方法的主要逻辑如下,
1,获取通话记录组中的第一个Cursor以及该组中的通话记录条数。
Cursor c = (Cursor) getItem(position);if (c == null) { return;}int count = getGroupSize(position);
2,依次将号码等信息封装在ContactInfo, PhoneCallDetails以及CallLogListItemViewHolder类中。
3,控制是否显示分组,调用CallLogListItemViewHolder的showActions()函数是否显示新建联系人等信息(根据ContactInfo来决定)。
4,调用CallLogListItemViewHolder的setPhoto函数显示图标以及姓名等信息。
5,PhoneCallDetailsViews详细的显示PhoneCallDetails中的通话记录信息(通话时间以及归属地等等),并且PhoneCallDetailsViews
是包含于CallLogListItemViewHolder中的。
- 通话记录分析
- MTK 通话记录分析
- 分析通话记录数据库
- Android加载通话记录流程分析
- Android加载通话记录流程分析
- 通话记录
- 通话记录
- 分析通话记录信息是通过什么写入的 android 源码 保存通话记录
- Android 4.0 Contacts 通话记录界面的分析(源码)
- 列举通话记录
- 获取通话记录
- 删除通话记录
- 保存通话记录
- 查找通话记录
- 1164: 通话记录
- 通话记录 oj130
- oj 通话记录
- Android4.4 Telephony流程分析——拨号应用(Dialer)的通话记录加载过程
- 使用JavaScript操作DOM动态生成下拉列表
- 一文看懂自编码器----综述
- 使用convert命令改变图片的分辨率
- 匈牙利算法讲解与习题练习
- 第二周 体验复杂度
- 通话记录分析
- P、NP、NPC、NP-hard问题
- 一个tomcat部署多个项目
- 求一个n阶方阵对角线元素之和。
- HD-1527-取石子游戏(威佐夫博弈)
- JSP中session对象的应用--用户控制
- window快捷键
- 51nod 1742 开心的小Q
- 窥探Swift之别样的枚举类型