您的位置:首页 > 其它

安卓高效开发:联系人数据存储与操作基本

2013-12-05 12:04 411 查看
前段时间项目中使用到了与联系人模块的相关操作,研究了一点皮毛,抽空整理了一下,把相关操作写成了个库项目,主要为了复用,由于有base项目做参考,没有使用数据库批量操作的方法,闲言少叙,进正题~



联系人数据存储的四张表:

RawContact表
每行代表一个联系人,每个联系人都有唯一的rawContactId, 这个是联系人操作的主要API,如新添/删除一个联系人时,都是对该表进行操作,与这个联系人相关联的其它表信息,系统会自行建立/清除。

Contact表
类似于家族表,通常情况下每个联系人的rawContactId和ContactId相同,但遇到系统认为是同一家族的联系人时会合并,这样不同rawContactID的联系人可能会共享相同的contactID,本文不涉及这种情况。

MIME表
存储着大类的类型及对应ID,一个典型的MIME表如下:



[align=left] [/align]
这个表中存储的是联系人细节信息的类型,包括一个15个大类,其中通常会使用的大类类型如下:



可以看出,联系人中常用的大类(MIME)信息有14种,包括姓名、电话、电子邮件等,每个大类信息又包括很多子类,如电话(Phone)中有移动、家庭、工作等小类,这些信息都是保存在下面要讲到的Data表中。

Data表
每行代表联系人的一条信息,如移动电话、工作电话、家庭住址信息等,一个联系人在该表中可能有多行数据,这些数据都有id,谷歌建议保持这些id,也就是在编辑联系人使其内容变化的时候,使用update而不是delete/insert的操作进行更新。
对于Data表中的一行,是包含的大类信息还是小类信息呢,这个不同的大类有所不同,对于姓名、组织、头像等这样较为唯一的数据,Data表中通常使用一行存储整个大类,而对于其他如电话、电子邮件、地址等可能含有较多种类的,则Data表中每行存储一个小类信息。
下面简要分析下Data表的列构成及存储结构,Data表中的重要数据列如下表:



Data表中包含这些数据列,其中比较重要的列Data1/Data2/Data3、MIMETYPE、RAW_CONTACT_ID
RAW_CONTACT_ID:该行内容属于哪个联系人,一个联系人仅有一个rawContactID.
MIMETYPE : 决定该行存储的数据属于是哪个大类,是电话、电子邮件还是其他?
MIMETYPE决定的类型不同,DATA1~DATA15表示的值类型也就不同。以PHONE为例:



[align=left] [/align]
Data1 : 官方解释Data1列存储的是核心内容,就是比如电话类的号码、邮件类的邮件地址、 即时通讯类的号码等,也就是通常在编辑联系人时填入的内容。
Data2:存入的是子Type,以上图为例,MIME决定了该行是Phone大类数据,而Data2中则决定该行是Phone中哪个小类
的数据,比如是移动、家庭、还是其他,其它大类类似。
Data3:标签,表示的是当Data2定义不同的子类时,应该显示的标签内容,就是我们在联系人编辑模块上看到的标签字符串,系统已经定义好常用的,如果想使用自定义的标签字符串,需将Data2设定为BaseTypes.TYPE_CUSTOM,Data3的标签字符串才会被使用。
在Data表中的每一行数据,根据MIME标识的不同,不同的大类,DATA1~DATA15列有着不同的存储内容,我自己整理了一下,其中绿色标注的表示常用的数据项,



宽度不够,data7~data10如下



图中对于每种大类,data1~data15所表示的内容,注意:如前文所述,在Data表中,这些联系人的一个大类不一定只占据1行,像电话、邮件、邮寄地址等大类类可能含有很多小类,每个小类占据一行信息。而姓名和组织比较特殊,这种大类对于每个联系人都是固定的,只占据一行信息。

举例说明

增加联系人,比如对于如下的联系人信息







在每个表中都有哪些信息呢?可以查看下,
RawContact表:



[align=left] [/align]
当用户增加一个联系人时,需要向RawContact表中插入一行数据,得到的_id就是rawContactId,对于rawContact表的每次内容改动,系统都会自动在其它表建立关联信息,也就是说,对于添加和删除联系人这样的操作,上层应用仅需要对该表进行操作即可。
从上表看出,rawContactId是25,这个是我们进行数据库操作的基础,每增加一个联系人时,其rawContactId的值会第加,此时系统会在contact表中寻找,如果系统觉得新添加的rawContactId对应的联系人没有”老乡“,就会把新加联系人的contactId也第加存储,否则,两个不同的rawContactId会共用一个contactId,在联系人界面只显示一个人。
关于contactID和rawContactID的关系在此不详述,有兴趣的朋友可通过代码多次添加相同内容的联系人复现此类现象,联系人界面上的每个人,都有1个ContactId,当用户重复插入相同的数据组时,系统认为都属于这个家族的成员,因此会把部分大类信息如注释信息等叠加显示,但并不增加新的联系人,查看Contact表可以看到,每个Contact表的lookup内容都包含该家族所有的rawContactid,每一次的插入数据组都有一唯一的rawContactID与之对应。这种实现方式为系统默认,不在本文中进行讨论。
Contact表



[align=left] [/align]



[align=left] [/align]

宽度原因分成两个图,实际只有1条数据,需关注lookup项,通过contactId和lookup内容确定的Uri是是进行联系人查询的钥匙。
大头在...Data表,裁剪、拼凑后的Data表新鲜出炉



_id : Data表中每行数据的id
mimeType : 前文已述,每行数据的大类类型,数字含义请参与mime那张表。
raw_contact_id : 每个联系人的id,可以看出都是25,和上面两表吻合,表示这些数据是来自同一联系人。
data1 :放置核心数据,其实就是编辑内容。
data2 :子类型,具体数字含义,参阅每个ContactsContract.CommonDataKinds里的内部类。
data3 : label内容,系统会在data2为TYPE_CUTSOM(0)时,使用data3.

在进行任何操作前,别忘了在注册文件中的联系人读写权限加上。

添加联系人代码

/************************************添加联系人工具函数********************************/
public static final int INFO_TYPE_NONE = -1;

public static final int HEADINFO_TYPE_STRUCTUREDNAME = INFO_TYPE_NONE + 1;
public static final int HEADINFO_TYPE_ORGANIZATION = INFO_TYPE_NONE + 2;

public static final int BODYINFO_TYPE_PHONE = INFO_TYPE_NONE + 3;
public static final int BODYINFO_TYPE_EMAIL = INFO_TYPE_NONE + 4;
public static final int BODYINFO_TYPE_NICKNAME = INFO_TYPE_NONE + 5;
public static final int BODYINFO_TYPE_WEBSITE = INFO_TYPE_NONE + 6;
public static final int BODYINFO_TYPE_EVENT = INFO_TYPE_NONE + 7;
public static final int BODYINFO_TYPE_RELATION = INFO_TYPE_NONE + 8;
public static final int BODYINFO_TYPE_SIPADDRESS = INFO_TYPE_NONE + 9;
public static final int BODYINFO_TYPE_STRUCTUREDPOSTAL = INFO_TYPE_NONE + 10;
public static final int BODYINFO_TYPE_NOTE = INFO_TYPE_NONE + 11;

/**
* Header信息包含StructuredName和Organization两个大类信息,这两个大类信息均只占据Data表中一行
* @param cr 客户内容解析器
* @param rawContactid 待插入的联系人id
* @param content 待插入的StructuredName数据内容,需客户提供
* @param HeaderInfoType 待插入的大类信息类型:1 : StructuredName , 2:Organization
* a)插入StructuredName,则对于Data表中的数据列状况如下(data1列中存的是DISPLAY_NAME,依次类推):
* 1:DISPLAY_NAME  2:GIVEN_NAME  3:FAMILY_NAME
* 4:PREFIX        5:MIDDLE_NAME 6:SUFFIX
* 7:PHONETIC_GIVEN_NAME  8:PHONETIC_MIDDLE_NAME  9:PHONETIC_FAMILY_NAME
* b)插入Organization,则对于Data表中数据列的状况如下:
* 1:COMPANY      2: TYPE          3:LABEL
* 4:TITLE         5:DEPARTMENT     6:JOB_DESCRIPTION
* 7:SYMBOL        8:PHONETIC_NAME  9:OFFIC_LOCATION
*/
public static void importHeaderInfoToData(Context context,int rawContactid,HashMap<String, String> content,int HeaderInfoType) {
if (context == null || rawContactid < 0 || content == null) {
Log.e(TAG, "importHeaderInfoToData param check fail");
return;
}
ContentValues value = new ContentValues();
value.put(Data.RAW_CONTACT_ID, rawContactid);
switch (HeaderInfoType) {
case HEADINFO_TYPE_STRUCTUREDNAME:
value.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
break;
case HEADINFO_TYPE_ORGANIZATION:
value.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
break;
default:
break;
}
/**
* 将content中非空数据项导入CV中
*/
for (int i = 1; i < 10; i++) {
String keyName = "data" + i;
if (content.containsKey(keyName)) {
value.put(keyName, content.get(keyName));
}
}
Uri uri = null;
ContentResolver cr = context.getContentResolver();
if (cr != null) {
uri = cr.insert(android.provider.ContactsContract.Data.CONTENT_URI, value);
}
if (DEBUG) Log.d(TAG, "importHeaderInfoToData result = " + uri);
value.clear();
}

/**
* CommonBod部分包括电话、电子邮件、昵称、网站、联系事件、关系、sip地址、地址、注释
* 在Data表中,这八个大类信息存储有着共同特点。
* data1 : 有效信息,如电话号码、邮件地址、即时通讯号等,就是事件用户输入的数据内容。
* data2 : 信息类型,如Phone大类包含很多小类,如移动、家庭、工作等,其它大类也是如此。
* data3 : 标签,当信息类型时用户自定义时,该项纪录自定义的信息类型,如用户自定义一个标签:亲人号码。
* 把对这八类信息的插入组合到一个方法中,
*/
public static void importCommonBodyToData(Context context,int rawContactid,int bodytype,String content,int type,String label){
if (context == null || rawContactid < 0 || bodytype < 0 || content == null || type < 0 || label == null) {
Log.e(TAG, "importCommonBodyToData param check fail");
return;
}
ContentValues value = new ContentValues();
switch (bodytype) {
case BODYINFO_TYPE_PHONE:
value.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_EMAIL:
value.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_NICKNAME:
value.put(Data.MIMETYPE, Nickname.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_WEBSITE:
value.put(Data.MIMETYPE, Website.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_EVENT:
value.put(Data.MIMETYPE, Event.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_RELATION:
value.put(Data.MIMETYPE, Relation.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_SIPADDRESS:
value.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_STRUCTUREDPOSTAL:
value.put(Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
break;
case BODYINFO_TYPE_NOTE:
value.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
break;
default:
break;
}
value.put(Data.RAW_CONTACT_ID,rawContactid);
value.put(Data.DATA1, content);
//对于注释类型信息,无有效type值
if (bodytype != BODYINFO_TYPE_NOTE) {
value.put(Data.DATA2, type);
}
//标签仅对于自定义的信息类型有效
if (type == BaseTypes.TYPE_CUSTOM && bodytype != BODYINFO_TYPE_NOTE) {
value.put(Data.DATA3, label);
}
Uri uri = null;
ContentResolver cr = context.getContentResolver();
if (cr != null) {
uri = cr.insert(android.provider.ContactsContract.Data.CONTENT_URI, value);
}
if (DEBUG) Log.d(TAG, "importCommonBodyToData : bodytype is " + bodytype +" result = " + uri);
value.clear();
}

//头像。
public static void importPhotoToData(Context context,int rawContactid,int drawableId){
if (context == null || rawContactid < 0 || drawableId < 0) {
Log.e(TAG, "importPhotoToData param check fail");
return;
}
Bitmap sourceBitmap = BitmapFactory.decodeResource(context.getResources(),drawableId);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
sourceBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
byte[] avatar = os.toByteArray();
ContentValues value = new ContentValues();
value.put(Data.RAW_CONTACT_ID, rawContactid);
value.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
value.put(Photo.PHOTO, avatar);

Uri uri = null;
ContentResolver cr = context.getContentResolver();
if (cr != null) {
uri = cr.insert(ContactsContract.Data.CONTENT_URI,value);
}
if (DEBUG) Log.d(TAG, "importPhotoToData result = " + uri);
value.clear();
}
//即时通信
public static void importIMToData(Context context,int rawContactid,int protocol,String content,int type,String label){
if (context == null || rawContactid < 0 || protocol < 0 || content == null || type < 0 || label == null) {
Log.e(TAG, "importIMToData param check fail");
return;
}
ContentValues value = new ContentValues();
value.put(Data.RAW_CONTACT_ID, rawContactid);
value.put(Data.MIMETYPE, Im.CONTENT_ITEM_TYPE);
value.put(Im.PROTOCOL, protocol);
value.put(Im.DATA,content);
value.put(Im.TYPE, type);
if (type == Im.TYPE_CUSTOM) {
value.put(Im.LABEL, label);
}
Uri uri = null;
ContentResolver cr = context.getContentResolver();
if (cr != null) {
uri = cr.insert(android.provider.ContactsContract.Data.CONTENT_URI, value);
}
if (DEBUG) Log.d(TAG, "importIMToData result = " + uri);
value.clear();
}
//组成员关系随情况扩展。

删除查询更新联系人

上述是联系人添加相关的工具类代码,关于删除和查询,由于实现过程类似,放到一起。
联系人的删/查都包含两处意思,
1)删/查一个联系人的所有信息,主要操作RawContact表
2)删/查Data表中的某个或某些数据信息,主要操作Data表。
结合上述的数据库存储情况,直接上代码,见注释。
/************************************查询联系人工具函数********************************/
/**
* 查询Contact表,获取当前所有联系人的游标集,Contact表中每一行代表一个联系人
* @param context 上下文
* @param uri   查询表的URI,默认是查询Contact表
* @param projection 查询结果列,考虑性能优化,谷歌建议只查询需要的列
* @param sortOrder  查询结果排序,默认是安装显示名升序排列
* @return  返回查询到的Contact游标集
*/
public static Cursor QueryContactTable(Context context,Uri uri,String[] projection,String sortOrder) {
Cursor result = null;

Uri wrapUri = uri == null ? Contacts.CONTENT_URI : uri;

String[] WrapProjection = projection == null ?
new String[] { Contacts._ID, // 0
Contacts.DISPLAY_NAME, // 1
Contacts.STARRED, // 2
Contacts.TIMES_CONTACTED, // 3
Contacts.CONTACT_PRESENCE, // 4
Contacts.PHOTO_ID, // 5
Contacts.LOOKUP_KEY, // 6
Contacts.HAS_PHONE_NUMBER, // 7
Contacts.IN_VISIBLE_GROUP, // 8
} : projection;

String WrapSortOrder = sortOrder == null ? Contacts.DISPLAY_NAME + "  COLLATE LOCALIZED ASC " : sortOrder;
result = context.getContentResolver().query(wrapUri, WrapProjection, null, null,WrapSortOrder);
return result;
}
/**
* @param cursor 目标游标集
* @param position 查询在目标游标集里第position位置的数据行
* @param isCloseCursor 是否在查询完关闭游标
* @return 得到目标联系人的lookupUri,这个对于查询联系人信息非常关键。
*/
public static Uri getContactUri(Cursor cursor,int position,boolean isCloseCursor) {
if (null == cursor || cursor.getCount() <= 0 || cursor.getCount() <= position) {
if (DEBUG) Log.e(TAG, "getContactUri fail");
return null;
}
cursor.moveToPosition(position);
final long contactId = cursor.getLong(cursor.getColumnIndex(Contacts._ID));
if (DEBUG) Log.d(TAG, "getContactUri [" + position + "] contactId is " + contactId);
final String lookupKey = cursor.getString(cursor
.getColumnIndex(Contacts.LOOKUP_KEY));
if (DEBUG) Log.d(TAG, "getContactUri [" + position + "] lookupKey is " + lookupKey);
if (null != cursor && isCloseCursor) {
cursor.close();
}
return Contacts.getLookupUri(contactId, lookupKey);
}
/**
* 通过上文得到的lookupUri,查询Data表获取需要的细节信息。
* @param context 上下文
* @param lookupUri 具体联系人的lookupUri,由在Contact表中查询的contactId和lookup数据获得。
* @return 结果游标集
*/
public static final Cursor QueryDataTable(Context context, Uri lookupUri) {
if (context == null || lookupUri == null) {
return null;
}
final List<String> segments = lookupUri.getPathSegments();
if (segments.size() != 4) {
return null;
}

final long uriContactId = Long.parseLong(segments.get(3));
final String uriLookupKey = Uri.encode(segments.get(2));
final Uri dataUri = Uri.withAppendedPath(
ContentUris.withAppendedId(Contacts.CONTENT_URI, uriContactId),
Contacts.Data.CONTENT_DIRECTORY);
if (DEBUG) Log.d(TAG, "QueryDataTable : get dataUri is " + dataUri);

Cursor cursor = context.getContentResolver().query(dataUri,
new String[] { Contacts.Data._ID,Contacts.Data.RAW_CONTACT_ID,Contacts.Data.MIMETYPE,
Contacts.Data.DATA1,Contacts.Data.DATA2,Contacts.Data.DATA3,Contacts.LOOKUP_KEY},
null, null, null);
if (cursor.moveToFirst()) {
String lookupKey = cursor.getString(cursor
.getColumnIndex(Contacts.LOOKUP_KEY));
if (!lookupKey.equals(uriLookupKey)) {
// ID and lookup key do not match
cursor.close();
return null;
}
if (DEBUG) {
Log.d(TAG, "QueryDataTable has " + cursor.getCount() + "'s item,"
+ "DataTable is\n" + "_ID | RAW_CONTACT_ID | "
+ " MIMETYPE | DATA1 | DATA2 | DATA3 ");
do {
String lineContent = "";
for (int i = 0; i < 6; i++) {
lineContent += (cursor.getString(i) + " | ");
}
Log.i(TAG, lineContent);
} while (cursor.moveToNext());
}
return cursor;
} else {
cursor.close();
return null;
}
}

/************************************删除联系人工具函数********************************/
/**
* 删除一个联系人的全部信息,依然是通过lookupUri
* @param context
* @param uri
*/
public static void deleteContactInAll(Context context,Uri uri) {
if (context == null || uri == null) {
if (DEBUG) Log.e(TAG, "deleteContactInAll fail");
return;
}
ContentResolver cr = context.getContentResolver();
int deletedNum = 0;
if (cr != null) {
deletedNum = context.getContentResolver().delete(uri, null, null);
}
if (DEBUG) Log.d(TAG, "deleteContactInAll uri is  = " + uri + "deletedNum is " + deletedNum);
}
/**
* 删除一个联系人的某些信息,主要针对Data表中的一些数据项。
* @param context
* @param where
* @param selectionArgs
*/
public static void deleteContactInData(Context context,String where,String[] selectionArgs) {
if (context == null ||  where == null || selectionArgs == null) {
if (DEBUG) Log.e(TAG, "deleteContactInOne fail");
return;
}
ContentResolver cr = context.getContentResolver();
int deletedNum = 0;
if (cr != null) {
deletedNum = cr.delete(android.provider.ContactsContract.Data.CONTENT_URI,where,selectionArgs);
if (DEBUG) Log.d(TAG, "deleteContactInOne The number of deleted item : " + deletedNum);
}
}
}

为了验证这些工具函数有效性,需要写个测试程序,如下:

package com.klp.androidklpcontactbase;

import java.util.HashMap;

import com.klp.contactbase.ContactUtils;

import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Relation;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.CommonDataKinds.Event;
import android.provider.ContactsContract.CommonDataKinds.Nickname;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.SipAddress;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.Website;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class TestActivity extends Activity {

public static final String TAG = "Contact::TestActivity";

ContentResolver cr = null;
int AddedRawContactid = -1;
Context context;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
cr = getContentResolver();
}

private void addContactTest() {
ContentValues values = new ContentValues();
Uri rawContentUri = cr.insert(RawContacts.CONTENT_URI, values);
AddedRawContactid = (int) ContentUris.parseId(rawContentUri);
Log.i(TAG, "addContactTest : AddedRawContactid is " + AddedRawContactid);

//Header
ContactUtils.importHeaderInfoToData(getBaseContext(), AddedRawContactid, prepareNameInfo(), ContactUtils.HEADINFO_TYPE_STRUCTUREDNAME);
ContactUtils.importHeaderInfoToData(getBaseContext(), AddedRawContactid, prepareOrgInfo(), ContactUtils.HEADINFO_TYPE_ORGANIZATION);

//Phone
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_PHONE, "13911111111", Phone.TYPE_HOME, "label");
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_PHONE, "13922222222", Phone.TYPE_MOBILE, "label");

//Email
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_EMAIL, "abc@sina.com", Email.TYPE_HOME, "label");

//NickName
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_NICKNAME, "nick_default", Nickname.TYPE_DEFAULT, "label");

//WebSite
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_WEBSITE, "www.csdn.net", Website.TYPE_BLOG, "csdn blog");
//Event
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_EVENT, "1949/10/1", Event.TYPE_BIRTHDAY, "label");
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_EVENT, "2013/12/2", Event.TYPE_CUSTOM, "嫦娥3号发射");

//Relationship
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_RELATION, "football club player", Relation.TYPE_FRIEND, "label");

//SipAddr
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_SIPADDRESS, "sip:22444032@phonesystem.3cx.com",
SipAddress.TYPE_HOME, "label");

//STRUCTUREDPOSTAL
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_STRUCTUREDPOSTAL, "ShangHai XH district donghai Street 13A",
SipAddress.TYPE_HOME, "label");

//Note 仅仅在 Data1列中存储注释内容,1和label参数都是无效的。
ContactUtils.importCommonBodyToData(getApplicationContext(), AddedRawContactid,
ContactUtils.BODYINFO_TYPE_NOTE, "随便写两句注释", 1, "label");

//Photo
ContactUtils.importPhotoToData(getApplicationContext(), AddedRawContactid, R.drawable.ic_launcher);

//IM
ContactUtils.importIMToData(getApplicationContext(), AddedRawContactid,
Im.PROTOCOL_QQ, "987654321", Im.TYPE_WORK, "label");
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
switch (item.getItemId()) {
case R.id.action_add:
addContactTest();
break;
case R.id.action_query:
queryContactTest();
break;
case R.id.action_delete:
//			deleteContactInAllTest();
deleteContactInOneTest();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}

/**
* 客户需要重写函数,根据实际情况填写非空项,内容为空的可以不填。
* @return
*/
public static HashMap<String, String> prepareNameInfo() {
HashMap<String, String> content = new HashMap<String, String>();
content.put(StructuredName.DISPLAY_NAME, "John");
content.put(StructuredName.GIVEN_NAME, "Choi");//确保联系人首字母排序
/*		content.put(StructuredName.FAMILY_NAME, "test_FAMILY_NAME");
content.put(StructuredName.MIDDLE_NAME, "test_MIDDLE_NAME");
content.put(StructuredName.PREFIX, "test_PREFIX");
content.put(StructuredName.SUFFIX, "test_SUFFIX");
content.put(StructuredName.PHONETIC_GIVEN_NAME, "test_PHONETIC_GIVEN_NAME");
content.put(StructuredName.PHONETIC_MIDDLE_NAME, "test_PHONETIC_MIDDLE_NAME");
content.put(StructuredName.PHONETIC_FAMILY_NAME, "test_PHONETIC_FAMILY_NAME");	*/
return content;
}

/**
* 客户需要重写函数,根据实际情况填写非空项,内容为空的可以不填。
* @return
*/
public static HashMap<String, String> prepareOrgInfo() {
HashMap<String, String> content = new HashMap<String, String>();
content.put(Organization.COMPANY, "Microsoft");
/*		content.put(Organization.TYPE, "test_TYPE");
content.put(Organization.LABEL, "test_LABEL");*/
content.put(Organization.TITLE, "Engineer");
/*		content.put(Organization.DEPARTMENT, "test_DEPARTMENT");
content.put(Organization.JOB_DESCRIPTION, "test_JOB_DESCRIPTION");
content.put(Organization.SYMBOL, "test_SYMBOL");
content.put(Organization.PHONETIC_NAME, "test_PHONETIC_NAME");
content.put(Organization.OFFICE_LOCATION, "test_OFFICE_LOCATION");*/
return content;
}

private void queryContactTest(){
//搜索Contact表获取游标结果集
Cursor contactCursor = ContactUtils.QueryContactTable(getApplicationContext(), null, null, null);
//从Contact表结果集中得到第1位置(下标为0)的URI
Uri lookupurl = ContactUtils.getContactUri(contactCursor, 0, false);
Log.i(TAG, "the first lookupurl is " + lookupurl);
//查询data表中的数据
ContactUtils.QueryDataTable(getApplicationContext(), lookupurl);
}

private void deleteContactInAllTest(){
//搜索Contact表获取游标结果集
Cursor contactCursor = ContactUtils.QueryContactTable(getApplicationContext(), null, null, null);
//从Contact表结果集中得到第1位置(下标为0)的URI,并将其删除。
ContactUtils.deleteContactInAll(getApplicationContext(), ContactUtils.getContactUri(contactCursor, 0, true));
}

private void deleteContactInOneTest(){
ContactUtils.deleteContactInData(getApplicationContext(),
Data.RAW_CONTACT_ID + "=?", new String[]{String.valueOf(AddedRawContactid)});
}
}

测试例按照顺序执行增加查询和删除菜单项,得到的log信息如下,可以看到rawContactId是27不是之前的25,是因为我删除了之前的重测了两次。



至于联系人更新主要是应用逻辑的问题,比如需要在添加前判断,如果此项数据存在,则使用更新,不存在则使用添加,具体的使用场景及逻辑判断,请结合前文联系人添加相关自行扩展。注:不需要再写更新联系人工具函数,只要在添加工具函数里加分支即可。

小结

上面的代码都是工具类代码,自己写了个测试例文件已验证可用,联系人更新部分没有给出具体代码,是因为需要联系具体的使用逻辑,至于性能优化(数据库批量操作)、UI适配、业务逻辑,如需要请自行解决,这个可以作为库项目编成jar包,供以后在开发和联系人基本操作相关的应用时使用,友情分享~毫无技术支持~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐