因项目需求,需要将本产品的客服电话信息,默默写入用户手机通讯录中,作为一个程序员,内心是拒绝的,但仍要实现该功能。

1. 动态申请读写权限

首先,在 Maniifest.xml 文件中添加如下两个权限的声明:

<uses-permission android:name="android.permission.READ_CONTACTS"/>
  <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

然后在代码中需要用到读写权限时,动态申请权限:

requestRuntimePermission(new String[]{Manifest.permission.READ_CONTACTS,
		Manifest.permission.WRITE_CONTACTS}, new PermissionListener() {
	@Override
	public void onGranted() {
		//todo 处理接下来的流程
	}

	@Override
	public void onDenied(List<String> deniedPermission) {
		showToast("权限被拒绝");
	}

});

2. 判断该联系人是否存在,如果存在,仅追加手机号码;不存在,则新增该联系人的头像、姓名及手机号等相关信息。

/**
 * 添加联系人信息
 *
 */
public static void insertConstacts(Context context, String name, List<String> list) {
	try {
		//该店管家用户是否存在,存在的话,就追加号码,不存在新增联系人
		long rawContactId = getContactsId(context, name);
		ContentValues values = new ContentValues();

		if (rawContactId == 0 ) {
			//插入raw_contacts表,并获取_id属性
			Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
			ContentResolver resolver = context.getContentResolver();
			rawContactId = ContentUris.parseId(resolver.insert(uri, values));

			//插入data表
			uri = Uri.parse("content://com.android.contacts/data");

			//add Name
			values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
			values.put(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/name");
			values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
			resolver.insert(uri, values);
			values.clear();

			//写入头像
			Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_poraital);
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
			out.flush();
			out.close();
			values.clear();
			values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
			values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE);
			values.put(ContactsContract.CommonDataKinds.Photo.PHOTO, out.toByteArray());
			context.getContentResolver().insert(ContactsContract.Data.CONTENT_URI, values);
			
		}

		//写入手机号码
		values.clear();
		values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
        values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone1);
        values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
        //插入data表
        Uri uri = Uri.parse("content://com.android.contacts/data");
        context.getContentResolver().insert(uri, values);

	} catch (Exception e) {
		Log.i(TAG, "insertConstacts:  e = " + e.getMessage());
		e.printStackTrace();
	}

}

    /**
     * 判断某个手机号是否存在
     */
    public static boolean isThePhoneExist(Context context, String phoneNum) {
        //uri=  content://com.android.contacts/data/phones/filter/#
        Cursor cursor = null;
        try {
            Uri uri = Uri.parse("content://com.android.contacts/data/phones/filter/" + phoneNum);
            ContentResolver resolver = context.getContentResolver();
            cursor = resolver.query(uri, new String[]{ContactsContract.Data.DISPLAY_NAME},
                    null, null, null); //从raw_contact表中返回display_name
            if (cursor.moveToFirst()) {
                //Log.i(TAG, "name=" + cursor.getString(0) + " , phoneNum = " + phoneNum);
                cursor.close();
                return true;
            }
        } catch (Exception e) {
            //Log.i(TAG, "163 e =" + e.getMessage());
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }

        return false;
    }

3. 查询数据库中所有的联系人

/**
 * 获取所有联系人信息
 */
public static List<SysContactsListBean> getAllSysContacts(Context context) {
	List<SysContactsListBean> list = new ArrayList<>();
	try {
		ContentResolver resolver = context.getContentResolver();
		SysContactsListBean sysContactsListBean;
		Uri uri = Uri.parse("content://com.android.contacts/data/phones");

		Cursor cursor1 = resolver.query(uri,
				new String[]{ContactsContract.Data.RAW_CONTACT_ID,
						ContactsContract.Data.DISPLAY_NAME,
						ContactsContract.CommonDataKinds.Phone.NUMBER},
				null, null, null);

		while (cursor1.moveToNext()) {
			sysContactsListBean = new SysContactsListBean();
			sysContactsListBean.setCustomerId(cursor1.getLong(0));
			sysContactsListBean.setCustomerName(cursor1.getString(1));
			sysContactsListBean.setPhoneNumber(cursor1.getString(2));
			list.add(sysContactsListBean);
		}
	} catch (Exception e) {
		e.printStackTrace();
	}

	if (!CollectorUtils.isEmpty(list)) {
		Collections.sort(list, new Comparator<SysContactsListBean>() {
			@Override
			public int compare(SysContactsListBean o1, SysContactsListBean o2) {
				return o2.getCustomerName().compareTo(o1.getCustomerName());
			}
		});
	}
	return list;
}

4. 总结


还有几个表需要特别注意:

4.1 raw_contacts:存放联系人的 ID,_id属性为主键,声明为autoincrement,即不需要手动设置,其他属性也不需要手动设置就有默认值;display_name属性为姓名;sort_key属性可以用于查询后的排序

4.2 mimetypes:存放数据的类型,比如"vnd.android.cursor.item/name"表示“姓名”类型的数据,"vnd.android.cursor.item/phone_v2"表示“电话”类型的数据;

4.3 data:存放具体的数据;raw_contact_id 属性用来连接raw_contacts表,每条记录表示一个具体数据;raw_contact_id 需要重点记住,手机中显示的每一个联系人对应一个固定的raw_contact_id,raw_contact_id对应着 raw_contacts表的 _id ,他俩是相同的值,两个表之间的关系必须理清。(我刚开始就是没有理清表之间的关系,以及各个字段代表的意思,做起来就感觉很混乱,这3个表很重要)我们主要的数据(email、phone等)都存放在data表;

ps:有些手机系统下,data 表的 raw_contact_id 和 data 表的 raw_contacts表的 _id 不一定相同,如果用 后者的id 作为主键来追加手机号码,就会出现追加不成功的现象。切记,切记。