Android Contacts API Library written in Kotlin with Java interoperability. No more ContentProviders and cursors. Say goodbye to ContactsContract. Build your own contacts app!
v0.3.0 - Lots of improvements, with some breaking changes, and bug fixes
There are quite a bit of improvements and bug fixes in this release. Unfortunately, as a result, there are a handful of breaking changes... Fortunately, migrations are simple and documented π
π‘ New features
Enhanced API for moving any RawContacts to different Accounts (or null account); MoveRawContactsAcrossAccounts #168, documentation
π Bug fixes
When updating groups, the title is always redacted (replaced with asterisks "*") #281
Null pointer exception in Fields.all (on first use in multi-threaded API usage) #286
π οΈ Improvements
Add display name, account, and options properties to RawContactEntity #268
Support for RawContact IDs in AccountsQuery #276
π£ Improvements with breaking changes
Replace AccountsLocalRawContactsUpdate API with a better one #282
Remove Account restrictions for Group, GroupMembership, Relation, and Event #167
Support setting/removing Contact/RawContact Photos as part of Insert and Update API calls #119
Allow different Accounts for each RawContact in insert APIs #270
Move set Contact/RawContact Options functions to be part of Insert and Update APIs #120
Remove BlankRawContact entity #269
Generalize AccountsRawContactsQuery to RawContactsQuery #271
Move Contact link/unlink extensions to a dedicated API #138
β»οΈ Dependency upgrades
These should not affect you...
2023 June Dependency Upgrades (most notably AS Flamingo + AGP 8 + Gradle 8 + JDK 17 + SDK 33 + JitCI -> Circle CI) #274
π§ Migrating from 0.2.4 -> 0.3.0
Replace AccountsLocalRawContactsUpdate API with a better one #282
PREVIOUSLY, to move local RawContacts (those with null Account), you would use the AccountsLocalRawContactsUpdate API.
val updateResult = Contacts(context)
.accounts()
.updateLocalRawContactsAccount()
.addToAccount(account)
.localRawContacts(rawContacts)
.commit()
NOW, the above API has been replaced with MoveRawContactsAcrossAccounts. It is a much more powerful API that allows you to move local and non-local RawContacts across Accounts.
val moveResult = Contacts(context)
.accounts()
.move()
.rawContactsTo(account, rawContacts)
.commit()
Remove Account restrictions for Group, GroupMembership, Relation, and Event #167
PREVIOUSLY, the following data kinds were ignored during insert and update operations for local RawContacts (those that are not associated with an Account);
GroupMembership
Event
Relation
This meant that the insert and update APIs would not let you set the values of the above data kinds for local RawContacts... You might have noticed this and thought it was a bug. However, it was intentional π
Another artificially imposed "restriction" that existed was that GroupEntity.account was NOT nullable. This meant that the library did not allow you to insert new groups that were not associated with an account. You were also not able to use the GroupsQuery API to fetch only groups that had no account (local groups). Furthermore, groups that were fetched that did have a null account in the database table were assigned a non-null account with the value Account("null", "null) (that is bad).
NOW,
the aforementioned data kinds are no longer ignored during insert and update operations for local RawContacts
GroupEntity.account is nullable, allowing you to insert local groups
GroupsQuery API now supports fetching only local groups
Groups that have a null account in the database table are properly assigned a null account value
Support setting/removing Contact/RawContact Photos as part of Insert and Update API calls #119
PREVIOUSLY, to set /remove the Contact or RawContact photo, you would use one of these extension functions that immediately commits the changes directly into the database. These can only be used for Contact and RawContacts that are already inserted.
Move set Contact/RawContact Options functions to be part of Insert and Update APIs #120
PREVIOUSLY, to get/set Contact/RawContact options, you would use the extension functions provided in contacts.core.util.ContactOptions and contacts.core.util.RawContactOptions,
val contactOptions = contact.options(contactsApi)
val rawContactOptions = rawContact.options(contactsApi)
contact.setOptions(contactsApi, contactOptions.mutableCopy{ ... })
rawContact.options(contactsApi, rawContactOptions{ ... })
The above extension functions read/write directly from the database and blocked the UI thread. Those functions no longer exist.
NOW, you can directly get/set options through the Contact/RawContact entities and read/write APIs;
val contactOptions = contact.options // yes, this also existed prior to this version
val rawContactOptions = rawContact.options
Contacts(context)
.update()
.contacts(
contact.mutableCopy {
setOptions { ... }
}
)
.rawContacts(
rawContact.mutableCopy {
setOptions { ... }
}
)
.commit()
You are now also able to set the options of a new RawContact for insert APIs,
Now that BlankRawContact no longer exists, all you have to do is replace any references you may have to it with references to RawContact.
Generalize AccountsRawContactsQuery to RawContactsQuery #271
PREVIOUSLY, the AccountsRawContactsQuery had a very limited set of functions. Its sole purpose was to provide a way to get RawContacts directly via the BlankRawContact entity. It did not allow you to specify fields to include as it had a predefined set of included fields.
You may have used it like so,
val rawContacts = Contacts(context).accounts().queryRawContacts().find()
NOW, it has been refactored to the more powerful RawContactsQuery,
val rawContacts = Contacts(context).rawContactsQuery().find()
The advantage of using the new ContactLink and ContactUnlink APIs is that it allows you to use [link|unlink]WithPermission and commit[WithContext|Async] extensions in the same call =)