I'm developing an app to store informations about events, birthdays in particular. An interesting feature would be to import every birthday found in Google Contacts, displaying it immediately. The app uses a Room DB to keep the contacts in Event Objects (basically, an event is defined by a name, a surname [optional] and a birthdate, plus some other optional parameters)
Since i'm a bit confused about the contacts providers (not every manufacturer uses the Google contacts provider right?) and every answer i found was quite old and doesn't use Kotlin, i'd like to know how to basically complete this function (located in my main activity, where i can easily access the viewmodel and everything else)
// 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
}
While i can easily write phase 2 and 3, i'm rather confused about the best way to deal with the first phase. You can assume i already have the contacts permission granted, and all i want is a list of names, surnames and dates i can use to create the objects.
EDIT: so i modified the above code with my progress. I'm now able to take the names, split the names in name and surname, verify the duplicates and insert the contact in my db. The only thing left is retrieving the birthday and manage the fact that the year may not be specified. I'm using a LocalDate object to save the date, as you see.
// 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
}
So this is basically the function i was looking for. I'm sure it could be done better, but i had no particular references in Kotlin. It takes the contacts, check their name and their birthday and manages the case of the year missing. Also, i just manage the case of "name only" but not the "only last name" case.