我写一个Android应用程序有一个代表一个人(特别是父母或孩子的监护人)的数据类型。 我希望能够“进口”的相关数据从联系人数据库在Android设备领域。 (这应该是可选的,也就是说,它不会是家长/监护人已在联系人数据库的要求,也不如果他们增加新的家长/监护人将联系人数据库进行更新。)
到目前为止,我已经写代码,开始一个新的意向选择特定的联系人(使用Intent.ACTION_PICK)。 然后我得到一个URI表示在数据库中的特定联系。
不幸的是,我不知道下一步是什么。 看起来这应该是世界上最简单的做法,但显然不是。 我已经通过Android开发者网站上的文档阅读,我已经通过多个Android书看。 没有快乐。
我想获得的具体信息为:
该联系人的名字(第一个和最后分开如果可能的话)
接触的(主要)的电子邮件地址
联系人的手机号码
我想,这应该通过使用ContentResolver的查询是可能的,但我不知道如何使用URI做到这一点从意图返回。 大多数文档假定你有联系的ID,而不是联系人的URI。 另外,我不知道是什么样的领域,我可以把到投影的查询,假设这是连做我想做的正确途径。
这里是我的出发代码:
// In a button's onClick event handler:
Intent intent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);
startActivityForResult(intent, PICK_CONTACT);
// In onActivityResult:
if (resultCode == RESULT_OK) {
if (requestCode == PICK_CONTACT) {
contactURI = data.getData();
// NOW WHAT?
}
}
好吧,大量挖掘后,我发现我所相信的是答案。 该解决方案,我发现不同的根据您所使用的Android的API级别。 然而,他们没有漂亮可言,因此,如果有更好的解决方案,我很想知道。
在任何情况下,第一步是获取联系人的ID,做一个查询的URI从Intent.ACTION_PICK返回。 虽然我们在这里,我们也应该得到的显示名称,字符串代表的接触是否有电话号码或没有。 (我们将不再需要他们为现代化的解决方案,但我们需要他们为传统的解决方案。)
String id, name, phone, hasPhone;
int idx;
Cursor cursor = getContentResolver().query(contactUri, null, null, null, null);
if (cursor.moveToFirst()) {
idx = cursor.getColumnIndex(ContactsContract.Contacts._ID);
id = cursor.getString(idx);
idx = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
name = cursor.getString(idx);
idx = cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER);
hasPhone = cursor.getString(idx);
}
根据记录,从该URI返回的列是最通过在常数表示的字段的 ContactsContract.Profile类(包括从其他接口继承的常量)。 不包括的是PHOTO_FILE_ID,PHOTO_THUMBNAIL_URI,或PHOTO_URI( 但包括PHOTO_ID)。
现在,我们有ID,我们需要得到相关数据。 第一(和简单的)的解决方案是查询的实体。 实体的查询检索所有联系人数据的联系人或生接触的一次。 每一行代表一个原始联系,使用常量访问ContactsContract.Contacts.Entity 。 通常你只能用RAW_CONTACT_ID,DATA1和MIMETYPE关注。 不过,如果你想分开的第一和最后一个名称,名称MIME类型持有DATA2的第一个名字和DATA3姓氏。
您加载通过与MIMETYPE列相匹配的变量ContactsContract.CommonDataKinds常量; 例如,电子邮件的MIME类型是ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE 。
// Build the Entity URI.
Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon();
b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
URI contactUri = b.build();
// Create the projection (SQL fields) and sort order.
String[] projection = {
ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
ContactsContract.Contacts.Entity.DATA1,
ContactsContract.Contacts.Entity.MIMETYPE };
String sortOrder = ContactsContract.Contacts.Entity.RAW_CONTACT_ID + " ASC";
cursor = getContentResolver().query(contactUri, projection, null, null, sortOrder);
String mime;
int mimeIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.MIMETYPE);
int dataIdx = cursor.getColumnIndex(ContactsContract.Contacts.Entity.DATA1);
if (cursor.moveToFirst()) {
do {
mime = cursor.getString(mimeIdx);
if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)) {
email = cursor.getString(dataIdx);
}
if (mime.equalsIgnoreCase(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)) {
phone = cursor.getString(dataIdx);
}
// ...etc.
} while (cursor.moveToNext());
}
不幸的是,实体没有引入unti API 11(Android 3.0的,蜂窝),这意味着这个代码是与在市场上Android设备(在本文写作)的大致65%的不兼容。 如果你尝试,你会得到一个抛出:IllegalArgumentException从URI。
第二个解决方案是建立一个查询字符串,并为您要使用的每个数据类型的查询:
// Get phone number - if they have one
if ("1".equalsIgnoreCase(hasPhone)) {
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = "+ id,
null, null);
if (cursor.moveToFirst()) {
colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
phone = cursor.getString(colIdx);
}
cursor.close();
}
// Get email address
cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID + " = " + id,
null, null);
if (cursor.moveToFirst()) {
colIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS);
email = cursor.getString(colIdx);
}
cursor.close();
// ...etc.
很显然,这样会导致大量的单独的数据库查询,因此不建议出于效率的考虑。
我已经想出了解决的办法是尽量使用实体查询的版本,赶抛出:IllegalArgumentException,并把遗留代码catch块:
try {
// Build the Entity URI.
Uri.Builder b = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, id).buildUpon();
b.appendPath(ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);
// ...etc...
} catch (IllegalArgumentException e) {
// Get phone number - if they have one
if ("1".equalsIgnoreCase(hasPhone)) {
// ...etc...
} finally {
// If you want to display the info in GUI objects, put the code here
}
我希望这可以帮助别人。 并再次,如果有更好的方法来做到这一点,我所有的耳朵。
事实证明, 有一种更好的方式来做到这一点。
正如我所提到的,ContactsContract.Contacts.Entity类不可用,直到API 11.然而, ContactsContract.Data类是早在API 5个可用的方法,你可以使用这个类在很大程度上您所使用的实体类的方法相同。
我已经更新了我的代码。 这是非常相似的实体类的代码,并且大部分作品以同样的方式。 不过,我已经与我的手机上运行姜饼测试它,它工作得很好。
一个变化我不得不做出的是:似乎没有成为一个办法让ContactsContract.Data.RAW_CONTACT_ID
从最初的查询,该ID是不一样的,你从如获得ID ContactsContract.Contacts._ID
。 相反,我质疑的ContactsContract.Contacts.DISPLAY_NAME
常量,它是横跨几乎每一个ContactsContract类别一致。
这里是工作的代码:
Cursor cursor; // Cursor object
String mime; // MIME type
int dataIdx; // Index of DATA1 column
int mimeIdx; // Index of MIMETYPE column
int nameIdx; // Index of DISPLAY_NAME column
// Get the name
cursor = getContentResolver().query(params[0],
new String[] { ContactsContract.Contacts.DISPLAY_NAME },
null, null, null);
if (cursor.moveToFirst()) {
nameIdx = cursor.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME);
name = cursor.getString(nameIdx);
// Set up the projection
String[] projection = {
ContactsContract.Data.DISPLAY_NAME,
ContactsContract.Contacts.Data.DATA1,
ContactsContract.Contacts.Data.MIMETYPE };
// Query ContactsContract.Data
cursor = getContentResolver().query(
ContactsContract.Data.CONTENT_URI, projection,
ContactsContract.Data.DISPLAY_NAME + " = ?",
new String[] { name },
null);
if (cursor.moveToFirst()) {
// Get the indexes of the MIME type and data
mimeIdx = cursor.getColumnIndex(
ContactsContract.Contacts.Data.MIMETYPE);
dataIdx = cursor.getColumnIndex(
ContactsContract.Contacts.Data.DATA1);
// Match the data to the MIME type, store in variables
do {
mime = cursor.getString(mimeIdx);
if (ContactsContract.CommonDataKinds.Email
.CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)) {
email = cursor.getString(dataIdx);
}
if (ContactsContract.CommonDataKinds.Phone
.CONTENT_ITEM_TYPE.equalsIgnoreCase(mime)) {
phone = cursor.getString(dataIdx);
phone = PhoneNumberUtils.formatNumber(phone);
}
} while (cursor.moveToNext());
}
}
//Add a permission to read contacts data to your application manifest.
<uses-permission android:name="android.permission.READ_CONTACTS"/>
//Use Intent.ACTION_PICK in your Activity
Intent contactPickerIntent = new Intent(Intent.ACTION_PICK,
ContactsContract.CommonDataKinds.Phone.CONTENT_URI);
startActivityForResult(contactPickerIntent, RESULT_PICK_CONTACT);
//Then Override the onActivityResult() and retrieve the ID,Phone number and Name in the data.
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// check whether the result is ok
if (resultCode == RESULT_OK) {
// Check for the request code, we might be usign multiple startActivityForReslut
switch (requestCode) {
case RESULT_PICK_CONTACT:
Cursor cursor = null;
try {
String phoneNo = null ;
String name = null;
Uri uri = data.getData();
cursor = getContentResolver().query(uri, null, null, null, null);
cursor.moveToFirst();
int phoneIndex =cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
phoneNo = cursor.getString(phoneIndex);
textView2.setText(phoneNo);
} catch (Exception e) {
e.printStackTrace();
}
break;
}
} else {
Log.e("MainActivity", "Failed to pick contact");
}
}
注:Android 2.3的(API级别9)之前,对联系供应商进行查询(如上面所示)要求您的应用程序声明READ_CONTACTS权限(请参见安全和权限)。 然而,与Android 2.3,联系人开始/人应用程式授予您的应用程序从联系人提供商读取时,它会返回一个结果的临时许可。 临时许可只适用于要求特定的联系,所以无法查询比意图的URI中指定的其他联系人,除非你做声明READ_CONTACTS许可。