从 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" 的情况。
我正在开发一个应用程序来存储有关事件(尤其是生日)的信息。一个有趣的功能是导入在 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" 的情况。