相关内容:
- 联系人数据库结构
- 内容提供者(略)
官方文档:https://developer.android.com/guide/topics/providers/contacts-provider.html
1.数据库结构
相关表共三个:Contacts、RawContacts、Data
1.ContactsContract.Contacts 原始联系表
---- URI: ContactsContract.Contacts.CONTENT_URI
表示不同联系人的行,基于聚合的原始联系人行。
字段说明:
1.Contacts.CONTENT_DIRECTORY | Constant Value:"photo" |
2.Contacts.DISPLAY_PHOTO: | Constant Value:"display_photo" | 全尺寸照片
3.Contacts.PHOTO: | Constant Value:"data15" | 照片缩略图
4.Contacts.PHOTO_FILE_ID: | Constant Value:"data14" | 全尺寸照片的文件ID (用此查询,See: ContactsContract.DisplayPhoto)
2.ContactsContract.RawContacts联系人数据摘要表
---- URI: ContactsContract.RawContacts.CONTENT_URI
包含联系人数据摘要的行,针对特定用户帐户和类型。
字段说明:
1.RawContacts._ID - long | id 不解释 ,永远唯一
2.RawContacts.CONTACT_ID - long
3.RawContacts.AGGREGATION_MODE - int
4.RawContacts.DELETED - int | 是否删除 0:正常 , 1:删除
5.RawContacts.TIMES_CONTACTED - int | 联系的次数
6.RawContacts.LAST_TIME_CONTACTED - long | 最近联系时间
7.RawContacts.STARRED -int | 是否是常用联系人 0:不是 ,1:是
8.RawContacts.CUSTOM_RINGTONE - String | 自定义铃声
9.RawContacts.SEND_TO_VOICEMAIL - int | 是否使用语音信息 0:不是 ,1:是
10.RawContacts.ACCOUNT_NAME - String | 账户名称 与ACCOUNT_TYPE对应
11.RawContacts.ACCOUNT_TYPE - String | 账户类型 比如:Gmail
12.RawContacts.DATA_SET - String | 账户数据
13.RawContacts.SOURCE_ID - String |
14.RawContacts.VERSION - int | 当前版本号
15.RawContacts.DIRTY - int | 标记此条记录是否更改过
3.ContactsContract.Data 联系人详细信息表
---- URI: ContactsContract.Data.CONTENT_URI 或 ContactsContract.Phone.CONTENT_URI
包含原始联系人详细信息(例如电子邮件地址或电话号码)的行。
字段说明:
1.Data._ID - long
2.Data.MIMETYPE - String | 数据类型,详细见下段
3.Data.RAW_CONTACT_ID - long | 同RawContacts.CONTACT_ID
4.Data.IS_PRIMARY - int | 标记重要 0 or 1
5.Data.IS_SUPER_PRIMARY - int | 默认使用此条数据 0 or 1
6.Data.DATA_VERSION - int | 数据版本
7.Data.Data1 | 与MIMETYPE有关,当为Phone.CONTENT_ITEM_TYPE时,Data1 就是电话号码
8.Data.Data2~~Data.Data15
2.Data.MIMETYPE 参数列表 :
StructuredName.CONTENT_ITEM_TYPE
Phone.CONTENT_ITEM_TYPE
Email.CONTENT_ITEM_TYPE
Photo.CONTENT_ITEM_TYPE
Organization.CONTENT_ITEM_TYPE
Im.CONTENT_ITEM_TYPE
Nickname.CONTENT_ITEM_TYPE
Note.CONTENT_ITEM_TYPE
StructuredPostal.CONTENT_ITEM_TYPE
GroupMembership.CONTENT_ITEM_TYPE
Website.CONTENT_ITEM_TYPE
Event.CONTENT_ITEM_TYPE
Relation.CONTENT_ITEM_TYPE
SipAddress.CONTENT_ITEM_TYPE
2.查询联系人列表
知道表结构后就可以很快的查取数据了,这里不讲内容提供者如何查询,直接上代码
重点在getContactsList() 和 getContactList() 两个方法
getContactsList() 返回数据结构:ContactsBean(String name, List<String> number)的List集合
getContactList() 返回数据结构 :ContactBean(String name, String number)的List集合
- 看情况选择:getContactsList() 数据结构清晰 ,getContactList() 速度快
查询到数据后应注意:
- 查询后的联系人姓名可能为空
- 查询后的电话号码可能有空格
- 查询后的电话号码可能为空
- 查询后的电话号码可能不是数字
下面代码已经做了相应处理, 代码仅供参考,暂不考虑性能,自行优化(静态Bean类)
获取联系人列表流程
- 通过ContentResolver查询数据库, Uri可根据上表合理选择(此处选择Contacts表的uri),得到返回的cusor
- 遍历cusor获取所有的联系人姓名
- 根据联系人姓名查询其拥有的联系电话(不止一个),(此处选择Data表)
public static List<ContactsBean> getContactsList(Context context) {
Logger.d("SMSHelper", "-----------SMSHelper#getContactsList()----------");
Logger.d("SMSHelper", "开始查询 Contacts 表");
List<ContactsBean> list = new ArrayList<ContactsBean>();
ContactsBean bean = null;
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cursor == null && cursor.getCount() <= 0) {
return null;
}
Logger.d("SMSHelper", "cursor.getCount():" + cursor.getCount());
while (cursor.moveToNext()) {
bean = new ContactsBean();
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));//姓名
wirteNumbers(resolver, name, bean);
list.add(bean);
}
cursor.close();
return list;
}
/**
* 根据联系人姓名查询电话
* 并写入
*/
private static void wirteNumbers(final ContentResolver contentResolver, String name, final ContactsBean bean) {
Logger.d("SMSHelper", "开始查询 Data 表 : 查询联系人:" + name);
Cursor dataCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI,
new String[]{ContactsContract.Data.DATA1},
ContactsContract.Data.DISPLAY_NAME + "= ? ",
new String[]{name}, null);
if (dataCursor == null) {
Logger.w("SMSHelper", "dataCursor == null ");
return;
}
if (dataCursor.getCount() > 0) {
bean.setName(name);
Logger.w("SMSHelper", " 电话信息 -- size: " + dataCursor.getCount());
while (dataCursor.moveToNext()) {
String number = dataCursor.getString(dataCursor.getColumnIndex(ContactsContract.Data.DATA1));
if (TextUtils.isEmpty(number) || !TextUtils.isDigitsOnly(number = number.replace(" ", ""))) {
Logger.w("SMSHelper", " 电话信息(异常) -- number: " + number);
continue;
}
bean.getNumList().add(number);
Logger.w("SMSHelper", " 电话信息 -- number: " + number);
}
dataCursor.close();
} else {
Logger.w("SMSHelper", " 无电话信息 -- name: " + name);
}
return;
}
完整代码
package app.utils;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.telephony.SmsManager;
import android.text.TextUtils;
import com.google.common.collect.Lists;
/**
* 短信电话相关工具类<br/>
* 目录: <br/>
* 1.发送短信 sendSMS() <br/>
* 2.拨打电话短信 callPhone() <br/>
* 3.获取所有短信记录getAllSms() <br/>
* 4.获取所有联系人getContactsList() <br/>
* 5.获取所有联系人getContactList() : 与第4条所生成的数据结构不同 <br/>
* <b>注</b>:使用时请添加相关权限
*
* @author 赵LW QQ:739043667
*/
public class SMSHelper {
/**
* 发送短信 (需要权限SMS_Send)
*
* @param num 电话
* @param text 短信内容
*/
public static void sendSMS(String num, String text) {
SmsManager.getDefault().sendTextMessage(num, null, text, null, null);
}
public static void callPhone(Context context, String num) {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:" + num));
// 开启系统拨号器
context.startActivity(intent);
}
/**
* 获取所有联系人 需要权限
* <uses-permission android:name="android.permission.READ_CONTACTS" />
* 数据结构:ContactBean(String displayName, String numList)
*
* @param
* @return
*/
public static List<ContactBean> getContactList(Context context) {
Logger.d("SMSHelper", "-----------SMSHelper#getContactList()----------");
Logger.d("SMSHelper", "开始查询 Data 表");
List<ContactBean> list = new ArrayList<ContactBean>();
ContactsContract.Data._ID
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(ContactsContract.Data.CONTENT_URI, null, null, null, null);
if (cursor == null && cursor.getCount() <= 0) {
return null;
}
Logger.d("SMSHelper", "cursor.getCount():" + cursor.getCount());
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1));
//数据清洗
if (TextUtils.isEmpty(number)) {
continue;
}
if (!TextUtils.isDigitsOnly(number = number.replace(" ", ""))) {
continue;
}
if (TextUtils.isEmpty(name)) {
name = number;
}
list.add(new ContactBean(name, number));
}
cursor.close();
return list;
}
/**
* 获取所有联系人
* 数据结构:ContactsBean(String displayName, List<String> numList)
* <p/>
* 需要权限:
* <uses-permission android:name="android.permission.READ_CONTACTS" />
*
* @param
* @return List<ContactsBean>
*/
public static List<ContactsBean> getContactsList(Context context) {
Logger.d("SMSHelper", "-----------SMSHelper#getContactsList()----------");
Logger.d("SMSHelper", "开始查询 Contacts 表");
List<ContactsBean> list = new ArrayList<ContactsBean>();
ContactsBean bean = null;
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
if (cursor == null && cursor.getCount() <= 0) {
return null;
}
Logger.d("SMSHelper", "cursor.getCount():" + cursor.getCount());
while (cursor.moveToNext()) {
bean = new ContactsBean();
String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));//姓名
wirteNumbers(resolver, name, bean);
list.add(bean);
}
cursor.close();
return list;
}
/**
* 根据联系人姓名查询电话
* 并写入
*/
private static void wirteNumbers(final ContentResolver contentResolver, String name, final ContactsBean bean) {
Logger.d("SMSHelper", "开始查询 Data 表 : 查询联系人:" + name);
Cursor dataCursor = contentResolver.query(ContactsContract.Data.CONTENT_URI,
new String[]{ContactsContract.Data.DATA1},
ContactsContract.Data.DISPLAY_NAME + "= ? ",
new String[]{name}, null);
if (dataCursor == null) {
Logger.w("SMSHelper", "dataCursor == null ");
return;
}
if (dataCursor.getCount() > 0) {
bean.setName(name);
Logger.w("SMSHelper", " 电话信息 -- size: " + dataCursor.getCount());
while (dataCursor.moveToNext()) {
String number = dataCursor.getString(dataCursor.getColumnIndex(ContactsContract.Data.DATA1));
if (TextUtils.isEmpty(number) || !TextUtils.isDigitsOnly(number = number.replace(" ", ""))) {
Logger.w("SMSHelper", " 电话信息(异常) -- number: " + number);
continue;
}
bean.getNumList().add(number);
Logger.w("SMSHelper", " 电话信息 -- number: " + number);
}
dataCursor.close();
} else {
Logger.w("SMSHelper", " 无电话信息 -- name: " + name);
}
return;
}
/**
* 获取短信记录 (需要bean: Smsinfo.java和 Read_SMS权限)
* <p>
* <uses-permission android:name="android.permission.READ_SMS" />
*
* @return List<Smsinfo>
* @author zlw QQ:739043667
*/
public static List<SmsInfo> getAllSms(Context context) {
List<SmsInfo> smsinfos = new ArrayList<SmsInfo>();
Uri uri = Uri.parse("content://sms/");
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, new String[]{"address", "date", "type", "body"}, null, null, null);
SmsInfo smsinfo = null;
while (cursor.moveToNext()) {
String address = cursor.getString(0);
String date = cursor.getString(1);
String type = cursor.getString(2);
String body = cursor.getString(3);
smsinfo = new SmsInfo(address, date, type, body);
smsinfos.add(smsinfo);
}
cursor.close();
return smsinfos;
}
/**
* 短信信息Bean 类
*
* @param
* @author zlw QQ:739043667
* @return
*/
public static class SmsInfo {
String address;
String date;
String type;
String body;
public SmsInfo(String address, String date, String type, String body) {
super();
this.address = address;
this.date = date;
this.type = type;
this.body = body;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
}
/**
* 联系人集合Bean类
*/
public static class ContactsBean {
private String name;
private List<String> numList;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getNumList() {
return numList;
}
public void setNumList(List<String> numList) {
this.numList = numList;
}
public ContactsBean() {
numList = Lists.newArrayList();
}
public ContactsBean(String displayName, List<String> numList) {
super();
this.name = displayName;
if (numList == null) {
this.numList = Lists.newArrayList();
} else {
this.numList = numList;
}
}
@Override
public String toString() {
return "ContactsBean [name=" + name + ", numList=" + numList + "]";
}
}
/**
* 联系人Bean类
*/
public static class ContactBean {
private String name;
private String number;
public ContactBean() {
}
public ContactBean(String name, String number) {
this.name = name;
this.number = number;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "ContactBean{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
'}';
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
}
网友评论
ContactsContract.Data.CONTENT_URI,
null,
ContactsContract.Data.CONTACT_ID + "= ? AND " + ContactsContract.Data.MIMETYPE + "= ?",
new String[]{String.valueOf(contactId), ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE},
null
);