从 Kotlin 中的 Google 联系人中读取生日和姓名

Read birthday and name from Google Contacts in Kotlin

我正在开发一个应用程序来存储有关事件(尤其是生日)的信息。一个有趣的功能是导入在 Google 联系人中找到的每个生日,并立即显示。 该应用程序使用 Room DB 将联系人保存在事件对象中(基本上,事件由姓名、姓氏 [可选] 和生日以及其他一些可选参数定义)

因为我对联系人提供程序有点困惑(不是每个制造商都使用 Google 联系人提供程序,对吗?)而且我找到的每个答案都很旧并且不使用 Kotlin,我会想知道如何基本完成这个功能(位于我的主activity,在那里我可以轻松访问视图模型和其他所有内容)

  // Import the contacts from Google Contacts
    fun importContacts(): Boolean {
        // No permission. For now, just send an explanation toast
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, getString(R.string.missing_permission), Toast.LENGTH_SHORT).show()
            return false
        }

        // Phase 1: get every contact having at least a name and a birthday
        val contacts = getContacts()

        // Phase 2: convert the extracted data in an Event List, verify duplicates
        val events = mutableListOf<Event>()
        loop@ for (contact in contacts) {
            val splitterName = contact.key.split(",")
            var name: String
            var surname = ""
            var date = LocalDate.of(1970,1,1)
            when (splitterName.size) {
                // Not considering surname only contacts
                1 -> name = splitterName[0].trim()
                2 -> {
                    name = splitterName[1].trim()
                    surname = splitterName[0].trim()
                }
                else -> continue@loop
            }
            try { date = LocalDate.parse(contact.value) }
            catch (e: Exception) { continue }
            val event = Event(id = 0, name = name, surname = surname, originalDate = date)

            // Check if the event is duplicate with a query
            if (!homeViewModel.checkExisting(it.key, it.value) == 0) events.add(event)

        }

        // Phase 3: insert the remaining events in the db
        events.forEach {
            homeViewModel.insert(it)
        }

        return true
    }

    // Get the contacts and save them in a list
    private fun getContacts(): Map<String, String> {
        val nameBirth = mutableMapOf<String, String>()
        val resolver: ContentResolver = contentResolver;
        val cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)

        if (cursor != null) {
            if (cursor.count > 0) {
                while (cursor.moveToNext()) {
                    val name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_ALTERNATIVE))
                    val birth = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.))
                    nameBirth[name] = "???"
                }
            }
        }
        cursor?.close()
        return nameBirth
    }

虽然我可以轻松编写第 2 阶段和第 3 阶段,但我对处理第一阶段的最佳方式感到困惑。你可以假设我已经获得了联系人权限,我想要的只是一个我可以用来创建对象的名字、姓氏和日期的列表。


EDIT: 所以我根据我的进度修改了上面的代码。我现在可以获取姓名,将姓名拆分为姓名和姓氏,验证重复项并将联系人插入我的数据库中。剩下的唯一事情就是检索生日并处理可能未指定年份的事实。如您所见,我正在使用 LocalDate 对象来保存日期。

// Import the contacts from Google Contacts
fun importContacts(): Boolean {
    // No permission. For now, just send an explanation toast
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
        Toast.makeText(this, getString(R.string.missing_permission), Toast.LENGTH_SHORT).show()
        return false
    }

    // Phase 1: get every contact having at least a name and a birthday
    val contacts = getContacts()

    // Phase 2: convert the extracted data in an Event List, verify duplicates
    val events = mutableListOf<Event>()
    loop@ for (contact in contacts) {
        // Take the name and split it to separate name and surname
        val splitterName = contact.value[0].split(",")
        var name: String
        var surname = ""
        var date = LocalDate.of(1970,1,1)
        when (splitterName.size) {
            // Not considering surname only contacts, but considering name only
            1 -> name = splitterName[0].trim()
            2 -> {
                name = splitterName[1].trim()
                surname = splitterName[0].trim()
            }
            else -> continue@loop
        }

        try {
            // Missing year, put 2020 as a placeholder
            var parseDate = contact.value[1]
            if (contact.value[1].length < 8) parseDate = contact.value[1].replaceFirst("-", "2020")
            date = LocalDate.parse(parseDate)
        }
        catch (e: Exception) { continue }
        val event = Event(id = 0, name = name, surname = surname, originalDate = date)

        // The duplicate check is performed at entity level
        events.add(event)
    }

    // Phase 3: insert the remaining events in the db
    events.forEach {
        homeViewModel.insert(it)
    }

    return true
}

// Get the contacts and save them in a map
private fun getContacts(): Map<String, List<String>> {
    val nameBirth = mutableMapOf<String, List<String>>()

    // Retrieve name and id
    val resolver: ContentResolver = contentResolver
    val cursor = resolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null)
    if (cursor != null) {
        if (cursor.count > 0) {
            while (cursor.moveToNext()) {
                val id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID))
                val name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME_ALTERNATIVE))
                // Retrieve the birthday
                val bd = contentResolver
                val bdc: Cursor? = bd.query(ContactsContract.Data.CONTENT_URI, arrayOf(ContactsContract.CommonDataKinds.Event.DATA),
                    ContactsContract.Data.CONTACT_ID + " = " + id + " AND " + ContactsContract.Data.MIMETYPE + " = '" +
                            ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE + "' AND " + ContactsContract.CommonDataKinds.Event.TYPE +
                            " = " + ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY, null, ContactsContract.Data.DISPLAY_NAME
                )

                if (bdc != null) {
                    if (bdc.count > 0) {
                        while (bdc.moveToNext()) {
                            // Using a list as key will prevent collisions on same name
                            val birthday: String = bdc.getString(0)
                            val person = listOf<String>(name, birthday)
                            nameBirth[id] = person
                        }
                    }
                    bdc.close()
                }
            }
        }
    }
    cursor?.close()
    return nameBirth
}

这基本上就是我一直在寻找的功能。我确信它可以做得更好,但我在 Kotlin 中没有特别的参考资料。它获取联系人,检查他们的姓名和生日,并管理失踪年份的情况。另外,我只处理 "name only" 的情况,而不处理 "only last name" 的情况。