在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,在此为GroupingListAdapter对象,
mAdapter = ObjectFactory.newCallLogAdapter(getActivity(),this,
new ContactInfoHelper(getActivity(), currentCountryIso), mVoicemailPlaybackPresenter,
isShowingRecentsTab);
mRecyclerView.setAdapter(mAdapter);
3,调用fetchCalls方法开始查询通话记录
fetchCalls();
fetchCalls方法如下,
mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
调用CallLogQueryHandler的fetchCalls方法进行查询。
MsimCallLogFragment和CallLogFragment几乎完相似,其onCreate方法如下,
final Activity activity = getActivity(); //获取对应的Activity对象
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);//弹出菜单,这样才可以选择其他类型并进行查询
onCreateView主要逻辑如下,
1,调用updateFilterSpinnerViews方法加载弹出菜单,
updateFilterSpinnerViews();
2,调用ObjectFactory的newCallLogAdapter方法构造CallLogAdapter对象,并将CallLogAdapter对象设置为MsimCallLogFragment的Adapter,
String currentCountryIso = GeoUtil.getCurrentCountryIso(getActivity());
boolean isShowingRecentsTab = mLogLimit != NO_LOG_LIMIT || mDateLimit != NO_DATE_LIMIT;
mAdapter = ObjectFactory.newCallLogAdapter(
getActivity(),
this,
new ContactInfoHelper(getActivity(), currentCountryIso),
mVoicemailPlaybackPresenter,
isShowingRecentsTab);
mRecyclerView.setAdapter(mAdapter);
3,调用fetchCalls开始查询通话记录,
fetchCalls();
fetchCalls方法如下,
1, CallLogQueryHandler的fetchCalls方法进行查询,
if (mFilterSubSpinnerView.isEnabled()) {
int[] subId = SubscriptionManager.getSubId(mCallSubFilter);
if (subId != null) {
mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit, subId[0]);
} else {
mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
}
} else {
mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
}
并且默认的查询类型就是查询所有通话记录, mCallTypeFilter定义如下,
protected int mCallTypeFilter = CallLogQueryHandler.CALL_TYPE_ALL;
因此,初次进入该界面时,显示的是所有通话记录信息。
2,如果未查到对应的通话类型结果,调用updateEmptyMessage进行更新界面,
updateEmptyMessage(mCallTypeFilter);
弹出菜单界面如下,
菜单弹出之后,点击查询类型之后, MsimCallLogFragment对应的界面会显示查询类型的结果,这一过程是如何实现的呢?
updateFilterSpinnerViews方法主要逻辑如下,
1,设置弹出菜单,
ArrayAdapter filterStatusAdapter = new ArrayAdapter(
getActivity(), R.layout.msim_call_log_spinner_item,
SpinnerContent.setupStatusFilterContent(getActivity()));
mFilterStatusSpinnerView.setAdapter(filterStatusAdapter);
SpinnerContent的setupStatusFilterContent方法如下,
case INDEX_CALL_TYPE_ALL:
value = CallLogQueryHandler.CALL_TYPE_ALL;
label = context.getString(R.string.call_log_all_calls_header);
break;
case INDEX_CALL_TYPE_INCOMING:
value = CallLog.Calls.INCOMING_TYPE;
label = context.getString(R.string.call_log_incoming_header);
break;
case INDEX_CALL_TYPE_OUTGOING:
value = CallLog.Calls.OUTGOING_TYPE;
label = context.getString(R.string.call_log_outgoing_header);
break;
case INDEX_CALL_TYPE_MISSED:
value = CallLog.Calls.MISSED_TYPE;
label = context.getString(R.string.call_log_missed_header);
break;
由此可见,弹出菜单的值和CallLog.Calls中的通话记录类型值是一一对应的。
2,注册监听器,
mFilterStatusSpinnerView.setOnItemSelectedListener(mStatusSelectedListener);
SpinnerContent.setSpinnerContentValue(mFilterStatusSpinnerView, mCallTypeFilter);
mStatusSelectedListener定义如下,
private OnItemSelectedListener mStatusSelectedListener = new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
Log.i(TAG, "Status selected, position: " + position);
int type = ((SpinnerContent)parent.getItemAtPosition(position)).value;
if (type != mCallTypeFilter) {
mCallTypeFilter = type;
fetchCalls();
}
}
•••
这样,一旦弹出菜单的查询的通话类型被选中之后,就会回调onItemSelected方法,如果这次的类型和上次查询的类型不同,则给mCallTypeFilter重新赋值,然后调用fetchCalls方法查询。
MsimCallLogFragment的fetchCalls方法在全面已经论述过,也是调用CallLogQueryHandler的fetchCalls方法完成的。
CallLogSearchFragment对应的activity还是CallLogActivity。
进入通话记录搜索界面之后, CallLogActivity的prepareSearchView方法如下,
mSearchView = (SearchView) searchViewLayout.findViewById(R.id.search_view);
mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);//注册监听器
•••
mPhoneSearchQueryTextListener定义如下,
private final OnQueryTextListener mPhoneSearchQueryTextListener = new OnQueryTextListener() {
如果SearchView的text发生变化,则会调用mPhoneSearchQueryTextListener的onQueryTextChange方法,该方法如下,
if (mSearchFragment != null) {
mSearchFragment.setQueryString(newText);
}
mSearchFragment就是CallLogSearchFragment对象。
CallLogSearchFragment继承CallLogFragment,因此, CallLogSearchFragment还是比较简单,大部分逻辑都在CallLogFragment中完成了。
CallLogSearchFragment中有一个mQueryString变量,主要是保存SearchView中的text值,
private String mQueryString;
CallLogSearchFragment的setQueryString方法如下,
if (!TextUtils.equals(mQueryString, queryString)) { //这次查询和上次查询的字符不同
//避免每次都查询
mQueryString = queryString;//记录查询字符
if (mAdapter != null) {
mAdapter.setLoading(true);
mAdapter.setQueryString(mQueryString); //这一句好像没什么作用
if (TextUtils.isEmpty(queryString)) { //查询字符为空,就查询全部通话记录
mCallLogQueryHandler.fetchCalls(CallLogQueryHandler.CALL_TYPE_ALL);
} else {
mCallLogQueryHandler.fetchCalls(queryString);
}
}
}
最后也是调用CallLogQueryHandler的fetchCalls方法进行查询。
小结:
CallLogFragment, MsimCallLogFragment和CallLogSearchFragment这三个类最后都是调用CallLogQueryHandler的fetchCalls方法查询通话信息。CallLogQueryHandler中一共有7个fetchCalls方法,只是参数不同。