使用 Kotlin 从联系人列表中获取联系人信息

Posted

技术标签:

【中文标题】使用 Kotlin 从联系人列表中获取联系人信息【英文标题】:Getting contact information from contact list with Kotlin 【发布时间】:2021-11-21 16:38:45 【问题描述】:

我正在尝试使用 Kotlin 获取有关用户可以在联系人列表中选择的联系人的一些信息。我尝试了一些仅部分有效的教程。这是应该从MainActivity.kt调用startActivityForResult()的代码:

add_contact.setOnClickListener 
    val intent = Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI)
    startActivityForResult(intent, CONTACT_PICK_CODE)

以下是被覆盖的onActivityResult方法:

@SuppressLint("Range")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) 
    super.onActivityResult(requestCode, resultCode, data)

    // handle intent results || calls when user from Intent (Contact Pick) picks or cancels pick contact
    if (resultCode == RESULT_OK) 
        if (requestCode == CONTACT_PICK_CODE) 
            val cursor1: Cursor
            val cursor2: Cursor?

            // get data from intent
            val uri = data!!.data
            cursor1 = contentResolver.query(uri!!, null, null, null, null)!!
            if (cursor1.moveToFirst()) 
                // get contact details
                val contactId = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts._ID))
                val contactName = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                val contactThumbnail = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
                val idResults = cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
                val idResultHold = idResults.toInt()
                // check if contact has a phone number or not
                if (idResultHold == 1) 
                    cursor2 = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId,
                    null,
                    null
                    )
                    // a contact may have multiple phone numbers
                    var contactNumber : String = ""
                    while (cursor2!!.moveToNext()) 
                        // get phone number
                        contactNumber = cursor2.getString(cursor2.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER))
                    
                    Toast.makeText(this, "Contact number: $contactNumber", Toast.LENGTH_SHORT).show()
                    cursor2.close()
                
                cursor1.close()
            
        
     else 
        Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show()
    

打印“Contact number: ...”的行从不打印联系电话,这意味着while 循环永远不会运行。

所有其他变量(contactIdcontactName...)似乎每次都被分配了正确的值,但数字却没有。有谁知道是什么原因造成的?

【问题讨论】:

【参考方案1】:
cursor2 = > contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, ...

要返回电话号码,您需要将 ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI Uri 替换为 ContactsContract.CommonDataKinds.Phone.CONTENT_URI

您可以使用Set 来代替字符串,而不是使用重复的数字,因为;此 URI 返回电话存储以外的所有电话号码链接;比如谷歌、WhatsApp、..等。

@SuppressLint("Range")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) 
    super.onActivityResult(requestCode, resultCode, data)

    // handle intent results || calls when user from Intent (Contact Pick) picks or cancels pick contact
    if (resultCode == RESULT_OK) 
        if (requestCode == CONTACT_PICK_CODE) 
            val cursor1: Cursor
            val cursor2: Cursor?

            // get data from intent
            val uri = data!!.data
            cursor1 = contentResolver.query(uri!!, null, null, null, null)!!

            if (cursor1.moveToFirst()) 
                // get contact details
                val contactId =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts._ID))
                val contactName =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
                val contactThumbnail =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.PHOTO_THUMBNAIL_URI))
                val idResults =
                    cursor1.getString(cursor1.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))
                val idResultHold = idResults.toInt()
                // check if contact has a phone number or not
                if (idResultHold == 1) 
                    cursor2 = contentResolver.query(
//                            ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, // WRONG
                        ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                        null,
                        ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId,
                        null,
                        null
                    )

                    // a contact may have multiple phone numbers
                    val numbers = mutableSetOf<String>()
                    cursor2?.let 
                        while (cursor2.moveToNext()) 
                            val phoneNumber =
                                cursor2.getString(
                                    cursor2.getColumnIndex(
                                        ContactsContract.CommonDataKinds.Phone.NUMBER
                                    )
                                ).replace("-", "").replace(
                                    " ",
                                    ""
                                )  // Remove the dash sign & spaces from the numbers

                            numbers.add(phoneNumber)
                        
                        Toast.makeText(
                            this@MainActivity,
                            "$contactName $numbers",
                            Toast.LENGTH_LONG
                        ).show()

                        cursor2.close()
                    
                    cursor1.close()
                
            


        
     else 
        Toast.makeText(this, "Cancelled", Toast.LENGTH_SHORT).show()
    

注意:您使用的是已弃用的 API onActivityResult,该 API 已替换为 registerForActivityResult

这是一个使用它的演示:

class MainActivity : AppCompatActivity() 

    private val requestSinglePermission =
        registerForActivityResult(ActivityResultContracts.RequestPermission())  isGranted ->
            // Do something if permission granted
            if (isGranted) 
                Log.d("LOG_TAG", "permission granted by the user")

                // Do something as the permission is not granted
             else 
                Log.d("LOG_TAG", "permission denied by the user")
            
        


    private var allNumLauncher =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult())  result ->
            if (result.resultCode == Activity.RESULT_OK) 
                // There are no request codes
                val data = result.data?.data

                data?.let 
                    val cursor = contentResolver.query(
                        data,
                        null,
                        null,
                        null,
                        null
                    )

                    cursor?.let 
                        if (it.moveToFirst()) 
                            val name =
                                it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))

                            if (Integer.parseInt(
                                    it.getString(
                                        it.getColumnIndex(
                                            ContactsContract.Contacts.HAS_PHONE_NUMBER
                                        )
                                    )
                                ) > 0 // Check if the contact has phone numbers
                            ) 

                                val id =
                                    it.getString(it.getColumnIndex(ContactsContract.Contacts._ID))

                                val phonesCursor = contentResolver.query(
                                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                    null,
                                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id,
                                    null,
                                    null
                                )

                                val numbers = mutableSetOf<String>()
                                phonesCursor?.let 
                                    while (phonesCursor.moveToNext()) 
                                        val phoneNumber =
                                            phonesCursor.getString(
                                                phonesCursor.getColumnIndex(
                                                    ContactsContract.CommonDataKinds.Phone.NUMBER
                                                )
                                            ).replace("-", "").replace(" ", "")
                                        numbers.add(phoneNumber)
                                    
                                    Toast.makeText(
                                        this@MainActivity,
                                        "$name $numbers",
                                        Toast.LENGTH_LONG
                                    ).show()
                                    Log.d(TAG, "$name $numbers")
                                

                                phonesCursor?.close()

                             else 
                                Toast.makeText(
                                    this@MainActivity,
                                    "$name - No numbers",
                                    Toast.LENGTH_LONG
                                ).show()
                                Log.d(TAG, "$name - No numbers")
                            
                        

                        cursor.close()
                    

                
            
        


    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.READ_CONTACTS
            ) != PackageManager.PERMISSION_GRANTED
        ) 
            requestSinglePermission.launch(
                Manifest.permission.READ_CONTACTS
            )

        


        // get a contact:
        val intent = Intent(Intent.ACTION_PICK,
                ContactsContract.Contacts.CONTENT_URI) 
        allNumLauncher.launch(intent)

    

【讨论】:

感谢您的详细解答!我尝试了您的代码,但由于某种原因,它仅在我打开应用程序后第一次选择联系人时才有效。之后的时间,它只显示一个空列表。为什么会这样? @感谢您的评论。可能是此联系人没有关联的电话号码。我刚刚再次测试它,它可以多次使用。 这很奇怪。也许这与我的android版本有关?我有8.0。我看到实际上它很少起作用。也许它第一次工作只是一个巧合。对于某些联系人,它会返回正确的姓名和号码,而对于其他联系人(都有关联的电话号码)则不会。 但是,我仍然设法使代码工作,方法是部分使用@Jabbar 的答案,然后添加获取联系人姓名的代码(并“清理”电话号码字符串)。我非常感谢添加的使用registerForActivityResult 的演示,我将尽我所能制作我现在使用的代码。我将分配 50 个代表。这个答案,因为它是最有用的。谢谢。 @EnricoCortinovis 我在 Android 11 上测试过,但让我在模拟器上的 Android 8 上检查一下【参考方案2】:

试试这个从一个联系人那里获取多个电话号码

const val REQUEST_SELECT_PHONE_NUMBER = 1

fun selectContact() 
    // Start an activity for the user to pick a phone number from contacts
    val intent = Intent(Intent.ACTION_PICK).apply 
        type = CommonDataKinds.Phone.CONTENT_TYPE
    
    if (intent.resolveActivity(packageManager) != null) 
        startActivityForResult(intent, REQUEST_SELECT_PHONE_NUMBER)
    


override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent) 
    if (requestCode == REQUEST_SELECT_PHONE_NUMBER && resultCode == Activity.RESULT_OK) 
        // Get the URI and query the content provider for the phone number
        val contactUri: Uri = data.data
        val projection: Array<String> = arrayOf(CommonDataKinds.Phone.NUMBER)
        contentResolver.query(contactUri, projection, null, null, null).use  cursor ->
            // If the cursor returned is valid, get the phone number
            if (cursor.moveToFirst()) 
                val numberIndex = cursor.getColumnIndex(CommonDataKinds.Phone.NUMBER)
                val number = cursor.getString(numberIndex)
                // Do something with the phone number
                ...
            

         val c: Cursor =  managedQuery(contactUri, null, null, null, null)
         val name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))

        
    

【讨论】:

谢谢!这行得通。我也在尝试获取联系人姓名(就像我的起始代码一样),val contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)) 就在您获得联系人号码的代码行之后,但它似乎不起作用。在 Logcat 中,它说错误出现在该行,并说 确保在从中访问数据之前正确初始化光标。你能解释为什么这不起作用吗? 我认为你需要新的光标,(我已经更新了上面的代码。也看看这个answer 我试过了,但它似乎不起作用。它给出了val name = ... 行的错误,说 Caused by: android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size 1。每当应用程序到达那行代码时,它就会崩溃。我是否遗漏了什么,或者您知道是什么原因造成的吗?

以上是关于使用 Kotlin 从联系人列表中获取联系人信息的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Android 中的 Intent 中获取联系信息?

如何从联系人列表中获取姓名

如何从 Android 中的联系人列表中获取联系人?使用联系提供商

我们正在使用 Lync SDK 获取联系信息,但无法返回完整的电话号码列表

如何请求雅虎用户信息和联系人列表

Android从联系人列表中获取电话号码