联系人备份之VCard

本文代码基于Android 9.0

VCard概述

packages/apps/Contacts/src/com/android/contacts/vcard/ExportVCardActivity.java
packages/apps/Contacts/src/com/android/contacts/vcard/ImportVCardActivity.java
packages/apps/Contacts/src/com/android/contacts/vcard/VCardService.java
packages/apps/Contacts/src/com/android/contacts/vcard/ExportProcessor.java
packages/apps/Contacts/src/com/android/contacts/vcard/ImportProcessor.java
frameworks/opt/vcard/java/com/android/vcard/VCardParser.java
frameworks/opt/vcard/java/com/android/vcard/VCardConstants.java
frameworks/opt/vcard/java/com/android/vcard/VCardEntry.java

联系人应用提供了联系人导入/导出功能,此功能是将联系人数据转换成VCard格式数据保存到SD卡中,以方便联系人备份或移动

VCard文件是基于标签+内容的字符串定义,VCard在Android系统中专门用来在SD卡中存储联系人,VCard文件标签定义在VCardConstants类中,VCard文件和联系人的对应关系可查看VCardEntry类来获取

contacts_detail

VCard文件格式处理库使用的是framework中提供的vcard库,VCard库中有3个版本的vcard(2.1,3.0,4.0)协议,VCard库为其提供了相应的解析器,分别是VCardParser_V21,VCardParser_V30,VCardParser_V40,解析器都继承自VCardParser

导出联系人

contacts_detail

导出联系人流程如上图所述,当打开ExportVCardActivity,链接VCard服务,并且在onServiceConnected中打开选择联系人界面

1
2
3
4
5
6
@Override
public synchronized void onServiceConnected(ComponentName name, IBinder binder) {
......
// Have the user choose where vcards will be exported to
startActivityForResult(getCreateDocIntent(), REQUEST_CREATE_DOCUMENT);
}

待选择好需要导出的联系人之后,直接调用mService.handleExportRequest()开始执行整个联系人导出动作

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CREATE_DOCUMENT) {
if (resultCode == Activity.RESULT_OK && mService != null &&
......
// The connection object will call finish().
mService.handleExportRequest(request, new NotificationImportExportListener(
ExportVCardActivity.this));
}
......
}
}

导入联系人

contacts_detail

导入联系人流程如上图所述,打开ImportVCardActivity,从Intent中获取VCard资源文件路径,经过权限检查等uri合法性检查,直接开启导入动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
//从Intent中获取需要导入的VCard路径信息
Uri sourceUri = getIntent().getData();

// Reading uris from non-storage needs the permission granted from the source intent,
// instead of permissions from RequestImportVCardPermissionActivity. So skipping requesting
// permissions from RequestImportVCardPermissionActivity for uris from non-storage source.
if (isStorageUri(sourceUri) && RequestImportVCardPermissionsActivity
.startPermissionActivity(this, isCallerSelf(this))) {
return;
}

/*************** 此处对sourceUri的合法性做一些列判断和验证 ****************/

if (isCallerSelf(this)) {
//开始导入联系人
startImport(sourceUri, sourceDisplayName);
} else {
ImportVCardDialogFragment.show(this, sourceUri, sourceDisplayName);
}
}