本博客基于android7.1版本分析,仅用于沟通学习使用
整体流程总结
- 通过在ContactEditorBaseFragment中对option item link的点击跳转到ContactSelectionActivity界面
- 通过LoaderManager去查询contactsprovider中的数据,并分成两部分显示数据
- 上半部分显示系统建议合并的联系人最多显示4个,根据联系人的名字、电话号码和邮件是否相同来显示建议合并的联系人
- 下半部分显示当前数据库所有的联系人
- 当选定需要合并的联系人后,会判读当前联系人内容是否发生过改变,如果改变了就显示JoinContactConfirmationDialogFragment的dialog
- 通过ContactSaveService,它继承自IntentService,通过异步的方式去更新数据库信息
- 最后会使用第一个联系人的名字作为合并后联系人的名字
整体操作流程
合并前后数据库部分变化
整体流程图
部分重要方法说明
JoinContactListAdapter
@Override
public void configureLoader(CursorLoader cursorLoader, long directoryId) {
JoinContactLoader loader = (JoinContactLoader) cursorLoader;
...............
builder.appendQueryParameter("limit", String.valueOf(MAX_SUGGESTIONS));//MAX_SUGGESTIONS=4
builder.appendQueryParameter(RawContacts.ACCOUNT_TYPE,
SimContactsConstants.ACCOUNT_TYPE_SIM);
builder.appendQueryParameter(SimContactsConstants.WITHOUT_SIM_FLAG,
"true");
loader.setSuggestionUri(builder.build());//将构建好的查询suggestion联系人的uri放进loader中
// TODO simplify projection
loader.setProjection(getProjection(false));
final Uri allContactsUri;
if (!TextUtils.isEmpty(filter)) {
allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_FILTER_URI).buildUpon()
.appendEncodedPath(Uri.encode(filter))
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
.appendQueryParameter(RawContacts.ACCOUNT_TYPE,
SimContactsConstants.ACCOUNT_TYPE_SIM)
.appendQueryParameter(
SimContactsConstants.WITHOUT_SIM_FLAG, "true")
.build();
} else {
allContactsUri = buildSectionIndexerUri(Contacts.CONTENT_URI).buildUpon()
.appendQueryParameter(
ContactsContract.DIRECTORY_PARAM_KEY, String.valueOf(Directory.DEFAULT))
.appendQueryParameter(RawContacts.ACCOUNT_TYPE,
SimContactsConstants.ACCOUNT_TYPE_SIM)
.appendQueryParameter(
SimContactsConstants.WITHOUT_SIM_FLAG, "true")
.build();
}
loader.setUri(allContactsUri); //将查询所有联系人的uri放进loader中
.....
}
ContactSaveService
private void joinContacts(Intent intent) {
long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1);
long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1);
// Load raw contact IDs for all raw contacts involved - currently edited and selected
// in the join UIs.
long rawContactIds[] = getRawContactIdsForAggregation(contactId1, contactId2);
if (rawContactIds == null) {
Log.e(TAG, "Invalid arguments for joinContacts request");
return;
}
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
// For each pair of raw contacts, insert an aggregation exception
for (int i = 0; i < rawContactIds.length; i++) {
for (int j = 0; j < rawContactIds.length; j++) {
if (i != j) {
buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);//构建插入agg_exception表的数据
}
}
}
final ContentResolver resolver = getContentResolver();
// Use the name for contactId1 as the name for the newly aggregated contact.
final Uri contactId1Uri = ContentUris.withAppendedId(
Contacts.CONTENT_URI, contactId1); //使用第一个联系人的名字
final Uri entityUri = Uri.withAppendedPath(
contactId1Uri, Contacts.Entity.CONTENT_DIRECTORY);
Cursor c = resolver.query(entityUri,
ContactEntityQuery.PROJECTION, ContactEntityQuery.SELECTION, null, null);
if (c == null) {
Log.e(TAG, "Unable to open Contacts DB cursor");
showToast(R.string.contactSavedErrorToast);
return;
}
long dataIdToAddSuperPrimary = -1;
try {
if (c.moveToFirst()) {
dataIdToAddSuperPrimary = c.getLong(ContactEntityQuery.DATA_ID);
}
} finally {
c.close();
}
// Mark the name from contactId1 IS_SUPER_PRIMARY to make sure that the contact
// display name does not change as a result of the join.
if (dataIdToAddSuperPrimary != -1) {
Builder builder = ContentProviderOperation.newUpdate(
ContentUris.withAppendedId(Data.CONTENT_URI, dataIdToAddSuperPrimary));
builder.withValue(Data.IS_SUPER_PRIMARY, 1);
builder.withValue(Data.IS_PRIMARY, 1);
operations.add(builder.build());
}
boolean success = false;
// Apply all aggregation exceptions as one batch
try {
resolver.applyBatch(ContactsContract.AUTHORITY, operations);
showToast(R.string.contactsJoinedMessage); //弹一个toast:Contacts linked
success = true;
} catch (RemoteException | OperationApplicationException e) {
Log.e(TAG, "Failed to apply aggregation exception batch", e);
showToast(R.string.contactSavedErrorToast);
}
Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
if (success) {
Uri uri = RawContacts.getContactLookupUri(resolver,
ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
callbackIntent.setData(uri);
}
deliverCallback(callbackIntent);
}