添加联系人
创建联系人数据
通过ContentValues
创建联系人数据,联系人数据包括名字、昵称、头像、备注、电话号码、传真号码、地址、公司及职位、邮箱、网站等信息,代码如下:
/**
* 获取联系人数据
*/
private fun getContactData(rawContactId: Long): ContactData {
val data = ArrayList<ContentValues>()
// 名字
val nameValues = generateContentValues(rawContactId)
nameValues.put(
ContactsContract.CommonDataKinds.StructuredName.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)
val givenName = "given"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName)
val middleName = "middle"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName)
val familyName = "family"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName)
val name = "$givenName $middleName $familyName"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
data.add(nameValues)
// 昵称
val nickNameValues = generateContentValues(rawContactId)
nickNameValues.put(
ContactsContract.CommonDataKinds.Nickname.MIMETYPE,
ContactsContract.CommonDataKinds.Nickname.CONTENT_ITEM_TYPE
)
val nickName = "nickName"
nickNameValues.put(ContactsContract.CommonDataKinds.Nickname.NAME, nickName)
data.add(nickNameValues)
// 头像
val photoValues = generateContentValues(rawContactId)
photoValues.put(
ContactsContract.CommonDataKinds.Photo.MIMETYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
)
photoValues.put(
ContactsContract.CommonDataKinds.Photo.PHOTO,
try {
assets.open("photo.jpg")
} catch (e: Exception) {
e.printStackTrace()
null
}?.readBytes()
)
data.add(photoValues)
// 备注
val remarkValues = generateContentValues(rawContactId)
remarkValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE
)
val remark = "your remark"
remarkValues.put(ContactsContract.CommonDataKinds.Note.NOTE, remark)
data.add(remarkValues)
// 电话号码
val mobilePhoneNumberValues = generateContentValues(rawContactId)
mobilePhoneNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
mobilePhoneNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE
)
val mobilePhoneNumber = "13999999999"
mobilePhoneNumberValues.put(
ContactsContract.CommonDataKinds.Phone.NUMBER,
mobilePhoneNumber
)
data.add(mobilePhoneNumberValues)
// 工作传真号码
val workFaxNumberValues = generateContentValues(rawContactId)
workFaxNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
workFaxNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_WORK
)
val workFaxNumber = "66669999999"
workFaxNumberValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, workFaxNumber)
data.add(workFaxNumberValues)
// 工作电话号码
val workPhoneNumberValues = generateContentValues(rawContactId)
workPhoneNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
workPhoneNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_WORK
)
val workPhoneNumber = "6666666666"
workPhoneNumberValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, workPhoneNumber)
data.add(workPhoneNumberValues)
// 公司电话号码
val hostNumberValues = generateContentValues(rawContactId)
hostNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
hostNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_COMPANY_MAIN
)
val hostNumber = "99999999999"
hostNumberValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, hostNumber)
data.add(hostNumberValues)
// 住宅传真号码
val homeFaxNumberValues = generateContentValues(rawContactId)
homeFaxNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
homeFaxNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_FAX_HOME
)
val homeFaxNumber = "99996666666"
homeFaxNumberValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, homeFaxNumber)
data.add(homeFaxNumberValues)
// 住宅电话号码
val homePhoneNumberValues = generateContentValues(rawContactId)
homePhoneNumberValues.put(
ContactsContract.Contacts.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
)
homePhoneNumberValues.put(
ContactsContract.CommonDataKinds.Phone.TYPE,
ContactsContract.CommonDataKinds.Phone.TYPE_HOME
)
val homePhoneNumber = "99998888888"
homePhoneNumberValues.put(ContactsContract.CommonDataKinds.Phone.NUMBER, homePhoneNumber)
data.add(homePhoneNumberValues)
// 微信号
val weChatNumberValues = generateContentValues(rawContactId)
weChatNumberValues.put(
ContactsContract.CommonDataKinds.Im.MIMETYPE,
ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE
)
weChatNumberValues.put(
ContactsContract.CommonDataKinds.Im.PROTOCOL,
ContactsContract.CommonDataKinds.Im.PROTOCOL_CUSTOM
)
weChatNumberValues.put(ContactsContract.CommonDataKinds.Im.CUSTOM_PROTOCOL, "WeChat")
val weChatNumber = "wx66666666"
weChatNumberValues.put(ContactsContract.CommonDataKinds.Im.DATA, weChatNumber)
data.add(weChatNumberValues)
// 地址
val addressValues = generateContentValues(rawContactId)
addressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
)
addressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_OTHER
)
val addressCountry = "addressCountry"
addressValues.put(ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY, addressCountry)
val addressState = "addressState"
addressValues.put(ContactsContract.CommonDataKinds.StructuredPostal.REGION, addressState)
val addressCity = "addressCity"
addressValues.put(ContactsContract.CommonDataKinds.StructuredPostal.CITY, addressCity)
val addressStreet = "addressStreet"
addressValues.put(ContactsContract.CommonDataKinds.StructuredPostal.STREET, addressStreet)
val addressPostalCode = "addressPostalCode"
addressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE,
addressPostalCode
)
addressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
"$addressStreet $addressCity $addressState $addressCountry $addressPostalCode"
)
data.add(addressValues)
// 工作地址
val workAddressValues = generateContentValues(rawContactId)
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
)
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_WORK
)
val workAddressCountry = "workAddressCountry"
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY,
workAddressCountry
)
val workAddressState = "workAddressState"
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.REGION,
workAddressState
)
val workAddressCity = "workAddressCity"
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.CITY,
workAddressCity
)
val workAddressStreet = "workAddressStreet"
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.STREET,
workAddressStreet
)
val workAddressPostalCode = "workAddressPostalCode"
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE,
workAddressPostalCode
)
workAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
"$workAddressStreet $workAddressCity $workAddressState $workAddressCountry $workAddressPostalCode"
)
data.add(workAddressValues)
// 住宅地址
val homeAddressValues = generateContentValues(rawContactId)
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE
)
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.TYPE,
ContactsContract.CommonDataKinds.StructuredPostal.TYPE_HOME
)
val homeAddressCountry = "homeAddressCountry"
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY,
homeAddressCountry
)
val homeAddressState = "homeAddressState"
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.REGION,
homeAddressState
)
val homeAddressCity = "homeAddressCity"
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.CITY,
homeAddressCity
)
val homeAddressStreet = "homeAddressStreet"
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.STREET,
homeAddressStreet
)
val homeAddressPostalCode = "homeAddressPostalCode"
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE,
homeAddressPostalCode
)
homeAddressValues.put(
ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS,
"$homeAddressStreet $homeAddressCity $homeAddressState $homeAddressCountry $homeAddressPostalCode"
)
data.add(homeAddressValues)
// 公司及职位
val organizationValues = generateContentValues(rawContactId)
organizationValues.put(
ContactsContract.CommonDataKinds.Organization.MIMETYPE,
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE
)
organizationValues.put(
ContactsContract.CommonDataKinds.Organization.TYPE,
ContactsContract.CommonDataKinds.Organization.TYPE_WORK
)
val organization = "organization"
organizationValues.put(ContactsContract.CommonDataKinds.Organization.COMPANY, organization)
val title = "title"
organizationValues.put(ContactsContract.CommonDataKinds.Organization.TITLE, title)
data.add(organizationValues)
// 邮箱
val emailValues = generateContentValues(rawContactId)
emailValues.put(
ContactsContract.CommonDataKinds.Email.MIMETYPE,
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
)
val email = "yuriyshea@163.com"
emailValues.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
data.add(emailValues)
// 网站
val urlValues = generateContentValues(rawContactId)
urlValues.put(
ContactsContract.CommonDataKinds.Website.MIMETYPE,
ContactsContract.CommonDataKinds.Website.CONTENT_ITEM_TYPE
)
val url = "https://yuriyshea.com"
urlValues.put(ContactsContract.CommonDataKinds.Website.URL, url)
data.add(urlValues)
return ContactData(rawContactId, name, data)
}
以上方法返回的是ContactData
对象,ContactData
是对联系人数据的包装类,代码如下:
/**
* 联系人信息
* @param rawContactId 联系人raw_contact_id
* @param name 名字
* @param data 其它数据
*/
data class ContactData(
val rawContactId: Long,
val name: String,
val data: ArrayList<ContentValues>
)
拿到创建好的联系人数据之后,我们就可以开始创建联系人了。创建联系人有2种不同的方式,一种是跳转到系统添加联系人的页面,并将联系人数据传递给该页面,通过页面操作完成联系人的添加,另一种是通过ContentResolver
直接向系统联系人数据库表中写入数据。
通过系统联系人页面添加
通过Intent
跳转到系统创建联系人的页面,并将联系人数据传递到该页面,代码如下:
/**
* 跳转系统联系人界面添加新的联系人
*/
private fun addContactByIntent() {
val intent = Intent(Intent.ACTION_INSERT, ContactsContract.Contacts.CONTENT_URI)
// 先插入一条空的ContentValues(),用以获取raw_contact_id
val rawContactUri =
contentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, ContentValues())
?: return
val rawContactId = ContentUris.parseId(rawContactUri)
currentContactData = getContactData(rawContactId).apply {
intent.putExtra(ContactsContract.Intents.Insert.NAME, name)
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data)
}
try {
startActivity(intent)
} catch (e: Throwable) {
e.printStackTrace()
}
}
在系统创建联系人的页面,我们可以继续编辑一些信息,编辑好之后点击保存即可。
直接添加
通过ContentResolver
可以直接向联系人数据库表写入联系人数据,代码如下:
/**
* 直接添加新的联系人
* 需要android.permission.WRITE_CONTACTS权限
*/
private fun addContactDirectly() {
// 先插入一条空的ContentValues(),用以获取raw_contact_id
val rawContactUri =
contentResolver.insert(ContactsContract.RawContacts.CONTENT_URI, ContentValues())
?: return
val rawContactId = ContentUris.parseId(rawContactUri)
val contactData = getContactData(rawContactId)
currentContactData = contactData
contentResolver.bulkInsert(
ContactsContract.Data.CONTENT_URI,
contactData.data.toTypedArray()
)
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show()
}
通过这种方式添加联系人,需要注意的地方有:
- 需要应用获取写联系人数据的权限,即
android.permission.WRITE_CONTACTS
,否则会报如下错误:
java.lang.SecurityException: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord{bef57ba 27869:com.xy.android/u0a2623} (pid=27869, uid=12623) requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS
- 创建
ContentValues
对象时,必须设置ContactsContract.Data.RAW_CONTACT_ID
字段的值,否则会报如下错误:
java.lang.IllegalArgumentException: raw_contact_id is required
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.bulkInsert(ContentProviderNative.java:503)
at android.content.ContentResolver.bulkInsert(ContentResolver.java:1935)
修改联系人
和创建联系人一样,修改联系人也有2中不同的方式,一种是将联系人数据传递给系统选择联系人的页面,在该页面选择要修改的联系人之后,会进一步跳转到系统编辑联系人页面,通过页面操作完成联系人的修改,另一种是通过ContentResolver
直接修改系统联系人数据库表中的数据。
通过系统联系人页面修改
通过Intent
跳转到系统选择联系人的页面,选择联系人之后再跳转到编辑联系人页面,代码如下:
/**
* 跳转系统联系人界面更新现有联系人
*/
private fun updateContactByIntent() {
val intent = Intent(Intent.ACTION_INSERT_OR_EDIT)
.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE)
val rawContactId = currentContactData?.rawContactId
if (rawContactId == null) {
Toast.makeText(this, "无当前联系人信息,请先创建联系人", Toast.LENGTH_SHORT).show()
return
}
currentContactData = getContactData(rawContactId).apply {
intent.putExtra(ContactsContract.Intents.Insert.NAME, name)
intent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, data)
}
try {
if (intent.resolveActivity(packageManager) != null) {
startActivity(intent)
}
} catch (e: Throwable) {
e.printStackTrace()
}
}
直接修改
通过ContentResolver
可以直接更新联系人数据库表中的数据,代码如下:
/**
* 直接更新现有联系人
* 需要android.permission.WRITE_CONTACTS权限
*/
private fun updateContactDirectly() {
val rawContactId = currentContactData?.rawContactId
if (rawContactId == null) {
Toast.makeText(this, "无当前联系人信息,请先创建联系人", Toast.LENGTH_SHORT).show()
return
}
// 名字
val nameValues = generateContentValues(rawContactId)
nameValues.put(
ContactsContract.CommonDataKinds.StructuredName.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE
)
val random = Random.nextInt(1000)
val givenName = "given$random"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, givenName)
val middleName = "middle$random"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME, middleName)
val familyName = "family$random"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME, familyName)
val name = "$givenName $middleName $familyName"
nameValues.put(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
contentResolver.update(
ContactsContract.Data.CONTENT_URI,
nameValues,
"${ContactsContract.Data.RAW_CONTACT_ID}=? and ${ContactsContract.Data.MIMETYPE}=?",
arrayOf(
rawContactId.toString(),
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
)
)
Toast.makeText(this, "更新成功", Toast.LENGTH_SHORT).show()
}
同样的,通过这种方式更新联系人,需要写联系人数据的权限(android.permission.WRITE_CONTACTS
)。
删除联系人
通过ContentResolver
删除联系人数据库表中的数据,代码如下:
/**
* 删除联系人
*/
private fun deleteContactDirectly() {
val rawContactId = currentContactData?.rawContactId
if (rawContactId == null) {
Toast.makeText(this, "无当前联系人信息,请先创建联系人", Toast.LENGTH_SHORT).show()
return
}
contentResolver.delete(
ContactsContract.Data.CONTENT_URI, "${ContactsContract.Data.RAW_CONTACT_ID}=?",
arrayOf(rawContactId.toString())
)
currentContactData = null
Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show()
}
通过ContentResolver
删除联系人数据也需要写联系人数据的权限(android.permission.WRITE_CONTACTS
)。
查找联系人
通过ContentResolver
查找联系人数据库表中的数据,代码如下:
/**
* 查找联系人
* 需要android.permission.READ_CONTACTS权限
*/
private fun queryContact() {
val rawContactId = currentContactData?.rawContactId
if (rawContactId == null) {
Toast.makeText(this, "无当前联系人信息,请先创建联系人", Toast.LENGTH_SHORT).show()
return
}
val cursor = contentResolver.query(
ContactsContract.Data.CONTENT_URI,
null,
"${ContactsContract.Data.RAW_CONTACT_ID}=?",
arrayOf(rawContactId.toString()),
null
)
if (cursor == null) {
Toast.makeText(this, "查找联系人失败", Toast.LENGTH_SHORT).show()
} else {
cursor.moveToFirst()
val displayName = cursor.getString(76)
Toast.makeText(this, "查找联系人成功,displayName: $displayName", Toast.LENGTH_SHORT)
.show()
}
cursor?.close()
}
通过ContentResolver
查找联系人需要读联系人数据的权限(android.permission.READ_CONTACTS
)。