获取特定联系人的信息从URI从Intent.ACTION_PICK返回(Get specific c

2019-07-17 15:29发布

我写一个Android应用程序有一个代表一个人(特别是父母或孩子的监护人)的数据类型。 我希望能够“进口”的相关数据从联系人数据库在Android设备领域。 (这应该是可选的,也就是说,它不会是家长/监护人已在联系人数据库的要求,也不如果他们增加新的家长/监护人将联系人数据库进行更新。)

到目前为止,我已经写代码,开始一个新的意向选择特定的联系人(使用Intent.ACTION_PICK)。 然后我得到一个URI表示在数据库中的特定联系。

不幸的是,我不知道下一步是什么。 看起来这应该是世界上最简单的做法,但显然不是。 我已经通过Android开发者网站上的文档阅读,我已经通过多个Android书看。 没有快乐。

我想获得的具体信息为:

  1. 该联系人的名字(第一个和最后分开如果可能的话)

  2. 接触的(主要)的电子邮件地址

  3. 联系人的手机号码

我想,这应该通过使用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?
    }
}

Answer 1:

好吧,大量挖掘后,我发现我所相信的是答案。 该解决方案,我发现不同的根据您所使用的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
}

我希望这可以帮助别人。 并再次,如果有更好的方法来做到这一点,我所有的耳朵。



Answer 2:

事实证明, 一种更好的方式来做到这一点。

正如我所提到的,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());
            }
        }


Answer 3:

//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许可。



文章来源: Get specific contact information from URI returned from Intent.ACTION_PICK