Convenience functions¶
This library provides some nice-to-have extensions in the contacts.core.utils
package. I will be
going over some of them in this page.
ℹ️ Functions in the util package that are used directly by other APIs such as result APIs are not discussed here.
Contact data getter and setters¶
Contacts can be made up of one or more RawContacts. In the case that a Contact has two or more RawContacts, getting/setting RawContact data may be a bit of a hassle, requiring loops or iterators,
// get all emails from all RawContacts belonging to the Contact
val contactEmails = contact.rawContacts.flatMap { it.emails }
// add an email to the first RawContact belonging to the Contact
contact.mutableCopy().rawContacts.first().emails.add(NewEmail())
ℹ️ For more info, read about API Entities.
To simplify things, getter/setter extensions are provided in the ContactData.kt
file,
// get all emails from all RawContacts belonging to the Contact
val contactEmailSequence = contact.emails()
val contactEmailList = contact.emailList()
// add an email to the first RawContact belonging to the Contact
contact.mutableCopy().addEmail {
address = "test@email.com"
}
ℹ️ Newer versions of the Android Open Source Project Contacts app and the Google Contacts app shows data coming from all RawContacts in a Contact details screen. However, they only allow editing a single RawContact and not the aggregate Contact in a single screen to avoid confusion. With this in mind, feel free to use the getter extensions but be very careful with using the setters!
Mutable and New RawContact data setters¶
Getting data from RawContacts is straightforward. You have direct access to their properties. The same goes for setting data.
val rawContactEmails = rawContact.emails
rawContact.mutableCopy().addEmail(
NewEmail().apply{
address = "abc@alphabet.com"
type = EmailEntity.Type.WORK
}
)
Still, there are some setter extensions provided in MutableRawContactData.kt
and
NewRawContactData.kt
that can add some sugar to your syntax.
The setter functions in this section and in the "Contact data getter and setters" section also uphold the redacted state of the mutable Contact/RawContact. We setting or adding a property using these extensions, the property being passed will be redacted if the Contact/RawContact it is being added to is redacted.
ℹ️ For more info, read Redact entities and API input and output in production.
Getting all data of a Contact or RawContact¶
There may be instances where you want to get all available data of a Contact or RawContact instance.
In such cases, you can use extensions in ExistingContactData.kt
, ExistingRawContactData.kt
, and
NewRawContactData.kt
.
To get a sequence of all data kinds of a Contact or RawContact,
To get it as a list,
Getting the parent Contact of a RawContact or Data¶
Using the Query
API, it is easy to get the parent Contact of a RawContact or Data,
val contactOfRawContact = contactsApi.query().where { Contact.Id equalTo rawContact.contactId }.find().firstOrNull()
val contactOfData = contactsApi.query().where { Contact.Id equalTo data.contactId }.find().firstOrNull()
ℹ️ For more info, read Query contacts (advanced).
To shorten things, you can use the extensions in RawContactContact.kt
and DataContact.kt
,
val contactOfRawContact = rawContact.contact(contactsApi)
val contactOfData = data.contact(contactsApi)
On a similar note, to get the parent RawContact of a Data using the extensions in DataRawContact.kt
,
These are blocking calls so you might want to do them outside the UI thread.
ℹ️ For more info, read Execute work outside of the UI thread using coroutines.
Refresh Contact, RawContact, and Data references¶
In-memory references to these entities could become inaccurate due to changes in the database that
could occur in your app, other apps, or by the Contacts Provider. If you need to get the most
up-to-date reference of an entity from the database, you could do it using the Query
and
DataQuery
APIs,
val contactFromDb = contactsApi.query().where { Contact.Id equalTo contactInMemory.id }.find().firstOrNull()
val rawContactFromDb = contactsApi.query().where { RawContact.Id equalTo rawContactInMemory.id }.find()
.firstOrNull()
?.rawContacts
?.find { it.id == rawContactInMemory.id }
val dataFromDb = contactsApi.data().query().where { DataId equalTo dataInMemory.id }.find().firstOrNull()
To shorten things, you can use extensions in ContactRefresh.kt
, RawContactRefresh.kt
, and
DataRefresh.kt
,
val contactFromDb = contactInMemory.refresh(contactsApi)
val rawContactFromDb = rawContactInMemory.refresh(contactsApi)
val dataFromDb = dataInMemory.refresh(contactsApi)
These are blocking calls so you might want to do them outside the UI thread.
ℹ️ For more info, read Execute work outside of the UI thread using coroutines.
Get/set Data read-only¶
To set the read-only property of all data of a NewRawContact
in one function call, you can use
extensions in NewRawContactData.kt
,
To check if any ExistingDataEntity
is read-only you can use extensions in DataIsReadOnly.kt
,
To check if multiple ExistingDataEntity
s are read-only,
These are blocking calls so you might want to do them outside the UI thread.
ℹ️ For more info, read Execute work outside of the UI thread using coroutines.
The value of
android.provider.ContactsContract.DataColumns.IS_READ_ONLY
is not a property ofExistingDataEntity
because including that particular column in the query projection array causes an exception. However, it is still possible to use the column in selection/WHERE clauses. Thus, allowing us to create this extension function =)ℹ️ For more info, read API Entities | Read-only data.
Sort Contacts by data fields¶
The Query
and BroadQuery
APIs allows you to sort Contacts based on fields in the Contacts table
such as Id
and DisplayNamePrimary
,
If you want to sort Contacts based on Data fields (e.g. email), you are unable to use the query
APIs provided in this library to do so. However, if you have a list of Contacts in memory, you can
use the extensions in ContactsComparator.kt
to build a Comparator
to use for sorting,
val sortedContacts = unsortedContacts.sortedWith(
Fields.Email.Address.desc(ignoreCase = true).contactsComparator()
)
You can also specify multiple fields for sorting,
val sortedContacts = unsortedContacts.sortedWith(
setOf(
Fields.Contact.Options.Starred.desc(),
Fields.Contact.DisplayNamePrimary.asc(ignoreCase = false),
Fields.Email.Address.asc()
).contactsComparator()
)
Get the Group of a GroupMembership¶
The GroupsQuery
allows you to get groups from a set of group Ids,
val group = contactsApi.groups().query().where { Id equalTo groupMembership.groupId }.find().firstOrNull()
val groups = contactsApi.groups().query().where { Id `in` groupMemberships.map { it.groupId } }.find()
To shorten things, you can use the extensions in GroupMembershipGroup.kt
,
These are blocking calls so you might want to do them outside the UI thread.
ℹ️ For more info, read Execute work outside of the UI thread using coroutines.