diff --git a/README.md b/README.md index adecb719..39964dbb 100644 --- a/README.md +++ b/README.md @@ -173,25 +173,25 @@ the results) ordered by display name in descending order, matching ALL of these ```kotlin val contacts = Contacts(context) .query() - .where( - (Fields.Name.GivenName startsWith "leo") and - ((Fields.Email.Address endsWith "gmail.com") or (Fields.Email.Address endsWith "hotmail.com")) and - (Fields.Address.Country equalToIgnoreCase "us") and - ((Fields.Event.Date lessThan Date().toWhereString()) and (Fields.Event.Type equalTo EventEntity.Type.BIRTHDAY)) and - (Fields.Contact.Options.Starred equalTo true) and - (Fields.Nickname.Name equalTo "DarEdEvil") and - (Fields.Organization.Company `in` listOf("facebook", "FB")) and - (Fields.Note.Note.isNotNullOrEmpty()) - ) + .where { + (Name.GivenName startsWith "leo") and + (Email.Address { endsWith("gmail.com") or endsWith("hotmail.com") }) and + (Address.Country equalToIgnoreCase "us") and + (Event { (Date lessThan Date().toWhereString()) and (Type equalTo EventEntity.Type.BIRTHDAY) }) and + (Contact.Options.Starred equalTo true) and + (Nickname.Name equalTo "DarEdEvil") and + (Organization.Company `in` listOf("facebook", "FB")) and + (Note.Note.isNotNullOrEmpty()) + } .accounts( Account("john.doe@gmail.com", "com.google"), Account("john.doe@myspace.com", "com.myspace"), ) - .include( - Fields.Contact.Id, - Fields.Contact.DisplayNamePrimary, - Fields.Phone.Number - ) + .include { setOf( + Contact.Id, + Contact.DisplayNamePrimary, + Phone.Number + ) } .orderBy(ContactsFields.DisplayNamePrimary.desc()) .offset(0) .limit(5) @@ -273,7 +273,7 @@ val emails = Contacts(context) .data() .query() .emails() - .where(Fields.Email.Address endsWith "gmail.com") + .where { Email.Address endsWith "gmail.com" } .orderBy(Fields.Email.Address.desc(ignoreCase = true)) .offset(0) .limit(20) diff --git a/async/src/main/java/contacts/async/data/DataQueryAsync.kt b/async/src/main/java/contacts/async/data/DataQueryAsync.kt index cbbf5d2a..110fdebd 100644 --- a/async/src/main/java/contacts/async/data/DataQueryAsync.kt +++ b/async/src/main/java/contacts/async/data/DataQueryAsync.kt @@ -1,6 +1,7 @@ package contacts.async.data import contacts.async.ASYNC_DISPATCHER +import contacts.core.AbstractDataFieldSet import contacts.core.DataField import contacts.core.data.DataQuery import contacts.core.entities.ExistingDataEntity @@ -15,7 +16,7 @@ import kotlin.coroutines.CoroutineContext * * See [DataQuery.find]. */ -suspend fun DataQuery.findWithContext( +suspend fun , E : ExistingDataEntity> DataQuery.findWithContext( context: CoroutineContext = ASYNC_DISPATCHER ): List = withContext(context) { find { !isActive } } @@ -28,6 +29,6 @@ suspend fun DataQuery.findWithCont * * See [DataQuery.find]. */ -fun DataQuery.findAsync( +fun , E : ExistingDataEntity> DataQuery.findAsync( context: CoroutineContext = ASYNC_DISPATCHER ): Deferred> = CoroutineScope(context).async { find { !isActive } } \ No newline at end of file diff --git a/core/src/main/java/contacts/core/BroadQuery.kt b/core/src/main/java/contacts/core/BroadQuery.kt index beda8354..3fa8a597 100644 --- a/core/src/main/java/contacts/core/BroadQuery.kt +++ b/core/src/main/java/contacts/core/BroadQuery.kt @@ -37,22 +37,40 @@ import contacts.core.util.unsafeLazy * ## Usage * * Here is an example query that returns the first 10 [Contact]s, skipping the first 5, where the - * any Contact data (e.g. name, email, address, phone, etc) matches the search term "john", ordered - * by the Contact display name primary (given name first) in ascending order (ignoring case). Only - * Contacts with at least one RawContact belonging to the given account and groups are included. - * Only the full name and email address attributes of the [Contact] objects are included. + * any Contact data (e.g. name, email, address, phone, note, etc) partially matches the search term + * "john", ordered by the Contact display name primary (given name first) in ascending order + * (ignoring case). Include only Contacts with at least one RawContact belonging to the given + * account and groups. Include only the name and email properties of [Contact]s. + * + * In Kotlin, * * ```kotlin - * import contacts.core.Fields.Name - * import contacts.core.Fields.Address - * import contacts.core.ContactsFields.DisplayNamePrimary + * val contacts : List = broadQuery. + * .accounts(account) + * .groups(groups) + * .include { Name.all + Address.all } + * .whereAnyContactDataPartiallyMatches("john") + * .orderBy(ContactsFields.DisplayNamePrimary.asc()) + * .offset(5) + * .limit(10) + * .find() + * ``` + * + * In Java, + * + * ```java + * import static contacts.core.Fields.*; + * import static contacts.core.OrderByKt.*; * * val contacts : List = broadQuery. * .accounts(account) * .groups(groups) - * .include(Name, Address) + * .include(new ArrayList<>() {{ + * addAll(Name.getAll()); + * addAll(Address.getAll()); + * }}) * .whereAnyContactDataPartiallyMatches("john") - * .orderBy(DisplayNamePrimary.asc()) + * .orderBy(asc(ContactsFields.DisplayNamePrimary)) * .offset(5) * .limit(10) * .find() @@ -270,6 +288,11 @@ interface BroadQuery { */ fun include(fields: Sequence): BroadQuery + /** + * See [BroadQuery.include]. + */ + fun include(fields: Fields.() -> Collection): BroadQuery + /** * Filters the [Contact]s partially matching the [searchString]. If not specified or null or * empty, then all [Contact]s are returned. @@ -334,6 +357,11 @@ interface BroadQuery { */ fun orderBy(orderBy: Sequence>): BroadQuery + /** + * See [BroadQuery.orderBy]. + */ + fun orderBy(orderBy: ContactsFields.() -> Collection>): BroadQuery + /** * Limits the maximum number of returned [Contact]s to the given [limit]. * @@ -460,6 +488,9 @@ private class BroadQueryImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun whereAnyContactDataPartiallyMatches(searchString: String?): BroadQuery = apply { // Yes, I know DEFAULT_SEARCH_STRING is null. This reads better though. this.searchString = searchString ?: DEFAULT_SEARCH_STRING @@ -478,6 +509,9 @@ private class BroadQueryImpl( } } + override fun orderBy(orderBy: ContactsFields.() -> Collection>) = + orderBy(orderBy(ContactsFields)) + override fun limit(limit: Int): BroadQuery = apply { this.limit = if (limit > 0) { limit diff --git a/core/src/main/java/contacts/core/Insert.kt b/core/src/main/java/contacts/core/Insert.kt index feaf7695..c2fe50c6 100644 --- a/core/src/main/java/contacts/core/Insert.kt +++ b/core/src/main/java/contacts/core/Insert.kt @@ -162,6 +162,11 @@ interface Insert { */ fun include(fields: Sequence): Insert + /** + * See [Insert.include]. + */ + fun include(fields: Fields.() -> Collection): Insert + /** * Adds a new [NewRawContact] to the insert queue, which will be inserted on [commit]. * The new instance is configured by the [configureRawContact] function. @@ -299,6 +304,9 @@ private class InsertImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun rawContact(configureRawContact: NewRawContact.() -> Unit): Insert = rawContacts(NewRawContact().apply(configureRawContact)) diff --git a/core/src/main/java/contacts/core/Query.kt b/core/src/main/java/contacts/core/Query.kt index 7c568068..4e3e03f6 100644 --- a/core/src/main/java/contacts/core/Query.kt +++ b/core/src/main/java/contacts/core/Query.kt @@ -29,22 +29,22 @@ import contacts.core.util.unsafeLazy * ## Usage * * Here is an example query that returns the first 10 [Contact]s, skipping the first 5, where the - * contact's name starts with "john" and has an email ending with "gmail", ordered by the name in - * ascending order (not ignoring case) and email (ignoring case) in descending order respectively. - * Only Contacts with at least one RawContact belonging to the given account are included. Only the - * full name and email address attributes of the [Contact] objects are included. + * contact's name starts with "john" and has an email ending with "gmail", order by favorite/starred + * status such that favorite/starred contacts appear first in the list AND order by display name + * primary in ascending order (from a to z ignoring case). Include only Contacts with at least one + * RawContact belonging to the given account. Include only name and email properties of [Contact]s. * * In Kotlin, * * ```kotlin - * import contacts.core.Fields.Name - * import contacts.core.Fields.Address - * * val contacts : List = query * .accounts(account) - * .include(Name, Address) - * .where((Name.DisplayName startsWith "john") and (Email.Address endsWith "gmail")) - * .orderBy(Name.DisplayName.asc(), Email.Address.desc(true)) + * .include { Name.all + Address.all } + * .where { (Name.DisplayName startsWith "john") and (Email.Address endsWith "gmail") } + * .orderBy( + * ContactsFields.Options.Starred.desc(), + * ContactsFields.DisplayNamePrimary.asc() + * ) * .offset(5) * .limit(10) * .find() @@ -59,9 +59,15 @@ import contacts.core.util.unsafeLazy * * List contacts = query * .accounts(account) - * .include(Name, Address) + * .include(new ArrayList<>() {{ + * addAll(Name.getAll()); + * addAll(Address.getAll()); + * }}) * .where(startsWith(Name.DisplayName, "john").and(endsWith(Email.Address, "gmail"))) - * .orderBy(asc(Name.DisplayName), desc(Email.Address, true)) + * .orderBy( + * desc(ContactsFields.Options.Starred), + * asc(ContactsFields.DisplayNamePrimary.asc) + * ) * .offset(5) * .limit(10) * .find(); @@ -190,6 +196,11 @@ interface Query { */ fun include(fields: Sequence): Query + /** + * See [Query.include]. + */ + fun include(fields: Fields.() -> Collection): Query + /** * Filters the [Contact]s matching the criteria defined by the [where]. If not specified or * null, then all [Contact]s are returned. @@ -250,6 +261,12 @@ interface Query { */ fun where(where: Where?): Query + /** + * Same as [Query.where] except you have direct access to all properties of [Fields] in the + * function parameter. Use this to shorten your code. + */ + fun where(where: Fields.() -> Where?): Query + /** * Orders the returned [Contact]s using one or more [orderBy]s. If not specified, then contacts * are ordered by ID in ascending order. @@ -301,6 +318,11 @@ interface Query { */ fun orderBy(orderBy: Sequence>): Query + /** + * See [Query.orderBy]. + */ + fun orderBy(orderBy: ContactsFields.() -> Collection>): Query + /** * Limits the maximum number of returned [Contact]s to the given [limit]. * @@ -415,11 +437,16 @@ private class QueryImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun where(where: Where?): Query = apply { // Yes, I know DEFAULT_WHERE is null. This reads better though. this.where = where ?: DEFAULT_WHERE } + override fun where(where: Fields.() -> Where?) = where(where(Fields)) + override fun orderBy(vararg orderBy: OrderBy) = orderBy(orderBy.asSequence()) override fun orderBy(orderBy: Collection>) = @@ -433,6 +460,9 @@ private class QueryImpl( } } + override fun orderBy(orderBy: ContactsFields.() -> Collection>) = + orderBy(orderBy(ContactsFields)) + override fun limit(limit: Int): Query = apply { this.limit = if (limit > 0) { limit diff --git a/core/src/main/java/contacts/core/Update.kt b/core/src/main/java/contacts/core/Update.kt index 2ed2e5f4..a8357827 100644 --- a/core/src/main/java/contacts/core/Update.kt +++ b/core/src/main/java/contacts/core/Update.kt @@ -150,6 +150,11 @@ interface Update { */ fun include(fields: Sequence): Update + /** + * See [Update.include]. + */ + fun include(fields: Fields.() -> Collection): Update + /** * Adds the given [rawContacts] to the update queue, which will be updated on [commit]. */ @@ -289,6 +294,9 @@ private class UpdateImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun rawContacts(vararg rawContacts: ExistingRawContactEntity) = rawContacts(rawContacts.asSequence()) diff --git a/core/src/main/java/contacts/core/Where.kt b/core/src/main/java/contacts/core/Where.kt index f522ea06..567f8f14 100644 --- a/core/src/main/java/contacts/core/Where.kt +++ b/core/src/main/java/contacts/core/Where.kt @@ -558,6 +558,9 @@ class Where private constructor( /** * Construct a copy of this where with the new field type determined by [substituteField]. */ + // Note that this function cannot be inlined because it is recursive (it calls itself). The + // compiler is smart enough to detect this. If the compiler allowed inlining a recursive + // function, then the code generation would look forever! Think about it! internal fun copyWithNewFieldType(substituteField: (Field) -> Field): Where { /* * Okay. Time for some "recursion" hehehe =). You know, I can't believe this interview diff --git a/core/src/main/java/contacts/core/accounts/AccountsRawContactsQuery.kt b/core/src/main/java/contacts/core/accounts/AccountsRawContactsQuery.kt index 9771d0f6..5adca298 100644 --- a/core/src/main/java/contacts/core/accounts/AccountsRawContactsQuery.kt +++ b/core/src/main/java/contacts/core/accounts/AccountsRawContactsQuery.kt @@ -32,15 +32,33 @@ import contacts.core.util.unsafeLazy * first 2, where the RawContact's display name starts with "a", ordered by the display name in * ascending order (ignoring case). * + * In Kotlin, + * * ```kotlin * val blankRawContacts = accountsRawContactsQuery * .accounts(account) - * .where(RawContactsFields.DisplayName startsWith "a") + * .where { DisplayNamePrimary startsWith "a" } * .orderBy(RawContactsFields.DisplayName.asc()) * .limit(5) * .offset(2) * .find() * ``` + * + * In Java, + * + * ```java + * import static contacts.core.RawContactsFields.*; + * import static contacts.core.WhereKt.*; + * import static contacts.core.OrderByKt.*; + * + * val blankRawContacts = accountsRawContactsQuery + * .accounts(account) + * .where(startsWith(DisplayNamePrimary, "a")) + * .orderBy(asc(RawContactsFields.DisplayName)) + * .limit(5) + * .offset(2) + * .find() + * ``` */ interface AccountsRawContactsQuery { @@ -78,6 +96,11 @@ interface AccountsRawContactsQuery { */ fun where(where: Where?): AccountsRawContactsQuery + /** + * See [AccountsRawContactsQuery.where]. + */ + fun where(where: RawContactsFields.() -> Where?): AccountsRawContactsQuery + /** * Orders the returned [BlankRawContact]s using one or more [orderBy]s. If not specified, then * data is ordered by ID in ascending order. @@ -100,6 +123,13 @@ interface AccountsRawContactsQuery { */ fun orderBy(orderBy: Sequence>): AccountsRawContactsQuery + /** + * See [AccountsRawContactsQuery.orderBy]. + */ + fun orderBy( + orderBy: RawContactsFields.() -> Collection> + ): AccountsRawContactsQuery + /** * Limits the maximum number of returned [BlankRawContact]s to the given [limit]. * @@ -216,6 +246,9 @@ private class AccountsRawContactsQueryImpl( this.where = where ?: DEFAULT_WHERE } + override fun where(where: RawContactsFields.() -> Where?) = + where(where(RawContactsFields)) + override fun orderBy(vararg orderBy: OrderBy) = orderBy(orderBy.asSequence()) override fun orderBy(orderBy: Collection>) = @@ -230,6 +263,9 @@ private class AccountsRawContactsQueryImpl( } } + override fun orderBy(orderBy: RawContactsFields.() -> Collection>) = + orderBy(orderBy(RawContactsFields)) + override fun limit(limit: Int): AccountsRawContactsQuery = apply { this.limit = if (limit > 0) { limit diff --git a/core/src/main/java/contacts/core/data/DataQuery.kt b/core/src/main/java/contacts/core/data/DataQuery.kt index f7bf5ba5..6f89f402 100644 --- a/core/src/main/java/contacts/core/data/DataQuery.kt +++ b/core/src/main/java/contacts/core/data/DataQuery.kt @@ -22,75 +22,75 @@ interface DataQueryFactory { /** * Queries for [Address]es. */ - fun addresses(): DataQuery + fun addresses(): DataQuery /** * Queries for [Email]s. */ - fun emails(): DataQuery + fun emails(): DataQuery /** * Queries for [Event]s. */ - fun events(): DataQuery + fun events(): DataQuery /** * Queries for [GroupMembership]s. */ - fun groupMemberships(): DataQuery + fun groupMemberships(): DataQuery /** * Queries for [Im]s. */ - fun ims(): DataQuery + fun ims(): DataQuery /** * Queries for [Name]s. */ - fun names(): DataQuery + fun names(): DataQuery /** * Queries for [Nickname]s. */ - fun nicknames(): DataQuery + fun nicknames(): DataQuery /** * Queries for [Note]s. */ - fun notes(): DataQuery + fun notes(): DataQuery /** * Queries for [Organization]s. */ - fun organizations(): DataQuery + fun organizations(): DataQuery /** * Queries for [Phone]s. */ - fun phones(): DataQuery + fun phones(): DataQuery // Photos are intentionally left out as it is internal to the library. /** * Queries for [Relation]s. */ - fun relations(): DataQuery + fun relations(): DataQuery /** * Queries for [SipAddress]es. */ - fun sipAddresses(): DataQuery + fun sipAddresses(): DataQuery /** * Queries for [Website]s. */ - fun websites(): DataQuery + fun websites(): DataQuery /** * Queries for custom data of type [E] with the given custom [mimeType]. */ - fun - customData(mimeType: MimeType.Custom): DataQuery + fun , E : ExistingCustomDataEntity> + customData(mimeType: MimeType.Custom): DataQuery } @Suppress("FunctionName") @@ -104,65 +104,66 @@ private class DataQueryFactoryImpl( private val isProfile: Boolean ) : DataQueryFactory { - override fun addresses(): DataQuery = DataQueryImpl( + override fun addresses(): DataQuery = DataQueryImpl( contacts, Fields.Address, MimeType.Address, isProfile ) - override fun emails(): DataQuery = DataQueryImpl( + override fun emails(): DataQuery = DataQueryImpl( contacts, Fields.Email, MimeType.Email, isProfile ) - override fun events(): DataQuery = DataQueryImpl( + override fun events(): DataQuery = DataQueryImpl( contacts, Fields.Event, MimeType.Event, isProfile ) - override fun groupMemberships(): DataQuery = + override fun groupMemberships(): DataQuery = DataQueryImpl( contacts, Fields.GroupMembership, MimeType.GroupMembership, isProfile ) - override fun ims(): DataQuery = DataQueryImpl( + override fun ims(): DataQuery = DataQueryImpl( contacts, Fields.Im, MimeType.Im, isProfile ) - override fun names(): DataQuery = DataQueryImpl( + override fun names(): DataQuery = DataQueryImpl( contacts, Fields.Name, MimeType.Name, isProfile ) - override fun nicknames(): DataQuery = DataQueryImpl( + override fun nicknames(): DataQuery = DataQueryImpl( contacts, Fields.Nickname, MimeType.Nickname, isProfile ) - override fun notes(): DataQuery = DataQueryImpl( + override fun notes(): DataQuery = DataQueryImpl( contacts, Fields.Note, MimeType.Note, isProfile ) - override fun organizations(): DataQuery = + override fun organizations(): DataQuery = DataQueryImpl( contacts, Fields.Organization, MimeType.Organization, isProfile ) - override fun phones(): DataQuery = DataQueryImpl( + override fun phones(): DataQuery = DataQueryImpl( contacts, Fields.Phone, MimeType.Phone, isProfile ) - override fun relations(): DataQuery = DataQueryImpl( + override fun relations(): DataQuery = DataQueryImpl( contacts, Fields.Relation, MimeType.Relation, isProfile ) - override fun sipAddresses(): DataQuery = DataQueryImpl( - contacts, Fields.SipAddress, MimeType.SipAddress, isProfile - ) + override fun sipAddresses(): DataQuery = + DataQueryImpl( + contacts, Fields.SipAddress, MimeType.SipAddress, isProfile + ) - override fun websites(): DataQuery = DataQueryImpl( + override fun websites(): DataQuery = DataQueryImpl( contacts, Fields.Website, MimeType.Website, isProfile ) @Suppress("UNCHECKED_CAST") - override fun - customData(mimeType: MimeType.Custom): DataQuery = DataQueryImpl( + override fun , E : ExistingCustomDataEntity> + customData(mimeType: MimeType.Custom): DataQuery = DataQueryImpl( contacts, - contacts.customDataRegistry.entryOf(mimeType).fieldSet as AbstractCustomDataFieldSet, + contacts.customDataRegistry.entryOf(mimeType).fieldSet as S, mimeType, isProfile ) } @@ -192,7 +193,7 @@ private class DataQueryFactoryImpl( * val addresses : List
= addressesQuery * .accounts(account) * .include(Fields.Address.FormattedAddress) - * .where((Fields.Address.Country equalTo "US") and (Fields.Address.PostCode startsWith "78")) + * .where { Address { (Country equalTo "US") and (PostCode startsWith "78") } } * .orderBy(Fields.Address.PostCode.asc()) * .offset(5) * .limit(10) @@ -202,17 +203,21 @@ private class DataQueryFactoryImpl( * In Java, * * ```java + * import static contacts.core.Fields.*; + * import static contacts.core.WhereKt.*; + * import static contacts.core.OrderByKt.*; + * * List
addresses = addressesQuery * .accounts(account) * .include(Address.FormattedAddress) * .where(equalTo(Address.Country, "US").and(startsWith(Address.PostCode, "78"))) - * .orderBy(Address.PostCode.asc()) + * .orderBy(asc(Address.PostCode)) * .offset(5) * .limit(10) * .find(); * ``` */ -interface DataQuery { +interface DataQuery, E : ExistingDataEntity> { /** * Limits this query to only search for data associated with one of the given [accounts]. @@ -224,17 +229,17 @@ interface DataQuery { * associated Account to be included in the search. RawContacts without an associated account * are considered local or device-only contacts, which are not synced. */ - fun accounts(vararg accounts: Account?): DataQuery + fun accounts(vararg accounts: Account?): DataQuery /** * See [DataQuery.accounts] */ - fun accounts(accounts: Collection): DataQuery + fun accounts(accounts: Collection): DataQuery /** * See [DataQuery.accounts] */ - fun accounts(accounts: Sequence): DataQuery + fun accounts(accounts: Sequence): DataQuery /** * Includes the given set of [fields] of type [F] in the resulting data objects of type [E]. @@ -277,17 +282,22 @@ interface DataQuery { * include/exclude in queries, inserts, and update, which will allow you to do things beyond * your wildest imagination! */ - fun include(vararg fields: F): DataQuery + fun include(vararg fields: F): DataQuery /** * See [DataQuery.include]. */ - fun include(fields: Collection): DataQuery + fun include(fields: Collection): DataQuery /** * See [DataQuery.include]. */ - fun include(fields: Sequence): DataQuery + fun include(fields: Sequence): DataQuery + + /** + * See [DataQuery.include]. + */ + fun include(fields: S.() -> Collection): DataQuery /** * Filters the returned data matching the criteria defined by the [where]. @@ -303,7 +313,12 @@ interface DataQuery { * use other fields such as [Fields.Contact] (perhaps to get all data of type [E] of a Contact), * [Fields.RawContact], [Fields.IsSuperPrimary], etc... */ - fun where(where: Where?): DataQuery + fun where(where: Where?): DataQuery + + /** + * See [DataQuery.where]. + */ + fun where(where: Fields.() -> Where?): DataQuery /** * Orders the returned data using one or more [orderBy]s. If not specified, then data is ordered @@ -313,31 +328,36 @@ interface DataQuery { * optional parameter. */ @SafeVarargs - fun orderBy(vararg orderBy: OrderBy): DataQuery + fun orderBy(vararg orderBy: OrderBy): DataQuery /** * See [DataQuery.orderBy]. */ - fun orderBy(orderBy: Collection>): DataQuery + fun orderBy(orderBy: Collection>): DataQuery /** * See [DataQuery.orderBy]. */ - fun orderBy(orderBy: Sequence>): DataQuery + fun orderBy(orderBy: Sequence>): DataQuery + + /** + * See [DataQuery.orderBy]. + */ + fun orderBy(orderBy: S.() -> Collection>): DataQuery /** * Limits the maximum number of returned data to the given [limit]. * * If not specified, limit value of [Int.MAX_VALUE] is used. */ - fun limit(limit: Int): DataQuery + fun limit(limit: Int): DataQuery /** * Skips results 0 to [offset] (excluding the offset). * * If not specified, offset value of 0 is used. */ - fun offset(offset: Int): DataQuery + fun offset(offset: Int): DataQuery /** * The list of [E]s. @@ -378,10 +398,10 @@ interface DataQuery { fun find(cancel: () -> Boolean): List } -private class DataQueryImpl( +private class DataQueryImpl, E : ExistingDataEntity>( private val contacts: Contacts, - private val defaultIncludeFields: FieldSet, + private val allFields: S, private val mimeType: MimeType, private val isProfile: Boolean, @@ -390,12 +410,12 @@ private class DataQueryImpl( // This allows us to append the RequiredDataFields, which casts T to AbstractDataField. private var rawContactsWhere: Where? = DEFAULT_RAW_CONTACTS_WHERE, private var include: Include = - Include(defaultIncludeFields.all + REQUIRED_INCLUDE_FIELDS), + Include(allFields.all + REQUIRED_INCLUDE_FIELDS), private var where: Where? = DEFAULT_WHERE, private var orderBy: CompoundOrderBy = DEFAULT_ORDER_BY, private var limit: Int = DEFAULT_LIMIT, private var offset: Int = DEFAULT_OFFSET -) : DataQuery { +) : DataQuery { override fun toString(): String = """ @@ -415,7 +435,7 @@ private class DataQueryImpl( override fun accounts(accounts: Collection) = accounts(accounts.asSequence()) - override fun accounts(accounts: Sequence): DataQuery = apply { + override fun accounts(accounts: Sequence): DataQuery = apply { rawContactsWhere = accounts.toRawContactsWhere() } @@ -423,9 +443,9 @@ private class DataQueryImpl( override fun include(fields: Collection) = include(fields.asSequence()) - override fun include(fields: Sequence): DataQuery = apply { + override fun include(fields: Sequence): DataQuery = apply { val includeFields = if (fields.isEmpty()) { - defaultIncludeFields.all.asSequence() + allFields.all.asSequence() } else { fields } @@ -433,16 +453,20 @@ private class DataQueryImpl( include = Include(includeFields + REQUIRED_INCLUDE_FIELDS) } - override fun where(where: Where?): DataQuery = apply { + override fun include(fields: S.() -> Collection) = include(fields(allFields)) + + override fun where(where: Where?): DataQuery = apply { // Yes, I know DEFAULT_WHERE is null. This reads better though. this.where = where ?: DEFAULT_WHERE } + override fun where(where: Fields.() -> Where?) = where(where(Fields)) + override fun orderBy(vararg orderBy: OrderBy) = orderBy(orderBy.asSequence()) override fun orderBy(orderBy: Collection>) = orderBy(orderBy.asSequence()) - override fun orderBy(orderBy: Sequence>): DataQuery = apply { + override fun orderBy(orderBy: Sequence>): DataQuery = apply { this.orderBy = if (orderBy.isEmpty()) { DEFAULT_ORDER_BY } else { @@ -450,7 +474,9 @@ private class DataQueryImpl( } } - override fun limit(limit: Int): DataQuery = apply { + override fun orderBy(orderBy: S.() -> Collection>) = orderBy(orderBy(allFields)) + + override fun limit(limit: Int): DataQuery = apply { this.limit = if (limit > 0) { limit } else { @@ -458,7 +484,7 @@ private class DataQueryImpl( } } - override fun offset(offset: Int): DataQuery = apply { + override fun offset(offset: Int): DataQuery = apply { this.offset = if (offset >= 0) { offset } else { diff --git a/core/src/main/java/contacts/core/data/DataUpdate.kt b/core/src/main/java/contacts/core/data/DataUpdate.kt index a9f5ce34..16154095 100644 --- a/core/src/main/java/contacts/core/data/DataUpdate.kt +++ b/core/src/main/java/contacts/core/data/DataUpdate.kt @@ -34,11 +34,21 @@ import contacts.core.util.unsafeLazy * * To update a set of [ExistingDataEntity]; * + * In Kotlin, + * * ```kotlin * val result = dataUpdate * .data(existingDataEntities) * .commit() * ``` + * + * In Java, + * + * ```java + * val result = dataUpdate + * .data(existingDataEntities) + * .commit() + * ``` */ interface DataUpdate { @@ -94,6 +104,11 @@ interface DataUpdate { */ fun include(fields: Sequence): DataUpdate + /** + * See [DataUpdate.include]. + */ + fun include(fields: Fields.() -> Sequence): DataUpdate + /** * Adds the given [data] to the update queue, which will be updated on [commit]. * @@ -201,6 +216,8 @@ private class DataUpdateImpl( } } + override fun include(fields: Fields.() -> Sequence) = include(fields(Fields)) + override fun data(vararg data: ExistingDataEntity) = data(data.asSequence()) override fun data(data: Collection) = data(data.asSequence()) diff --git a/core/src/main/java/contacts/core/groups/GroupsQuery.kt b/core/src/main/java/contacts/core/groups/GroupsQuery.kt index 9e243ca4..941cee77 100644 --- a/core/src/main/java/contacts/core/groups/GroupsQuery.kt +++ b/core/src/main/java/contacts/core/groups/GroupsQuery.kt @@ -25,16 +25,34 @@ import contacts.core.util.unsafeLazy * where the group's title starts with "a", ordered by the group title in ascending order * (ignoring case). * + * In Kotlin, + * * ```kotlin * val groups = groupsQuery * .accounts(account) - * .where(GroupsFields.Title startsWith "a") + * .where { Title startsWith "a" } * .orderBy(GroupsFields.Title.asc()) * .limit(5) * .offset(2) * .find() * ``` * + * In Java, + * + * ```kotlin + * import static contacts.core.GroupsFields.*; + * import static contacts.core.WhereKt.*; + * import static contacts.core.OrderByKt.*; + * + * val groups = groupsQuery + * .accounts(account) + * .where(startsWith(Title, "a")) + * .orderBy(asc(GroupsFields.Title)) + * .limit(5) + * .offset(2) + * .find() + * ``` + * * ## Developer notes * * Groups are inextricably linked to Accounts. A group must be assigned to an account, if an account @@ -75,6 +93,11 @@ interface GroupsQuery { */ fun where(where: Where?): GroupsQuery + /** + * See [GroupsQuery.where] + */ + fun where(where: GroupsFields.() -> Where?): GroupsQuery + /** * Orders the returned [Group]s using one or more [orderBy]s. If not specified, then groups * are ordered by ID in ascending order. @@ -97,6 +120,11 @@ interface GroupsQuery { */ fun orderBy(orderBy: Sequence>): GroupsQuery + /** + * See [GroupsQuery.orderBy]. + */ + fun orderBy(orderBy: GroupsFields.() -> Sequence>): GroupsQuery + /** * Limits the maximum number of returned [Group]s to the given [limit]. * @@ -210,6 +238,8 @@ private class GroupsQueryImpl( this.where = where ?: DEFAULT_WHERE } + override fun where(where: GroupsFields.() -> Where?) = where(where(GroupsFields)) + override fun orderBy(vararg orderBy: OrderBy) = orderBy(orderBy.asSequence()) override fun orderBy(orderBy: Collection>) = orderBy(orderBy.asSequence()) @@ -222,6 +252,9 @@ private class GroupsQueryImpl( } } + override fun orderBy(orderBy: GroupsFields.() -> Sequence>) = + orderBy(orderBy(GroupsFields)) + override fun limit(limit: Int): GroupsQuery = apply { this.limit = if (limit > 0) { limit diff --git a/core/src/main/java/contacts/core/profile/ProfileInsert.kt b/core/src/main/java/contacts/core/profile/ProfileInsert.kt index 4f628eb8..8298942c 100644 --- a/core/src/main/java/contacts/core/profile/ProfileInsert.kt +++ b/core/src/main/java/contacts/core/profile/ProfileInsert.kt @@ -188,6 +188,11 @@ interface ProfileInsert { */ fun include(fields: Sequence): ProfileInsert + /** + * See [ProfileInsert.include]. + */ + fun include(fields: Fields.() -> Sequence): ProfileInsert + /** * Configures a new [NewRawContact] for insertion, which will be inserted on [commit]. The * new instance is configured by the [configureRawContact] function. @@ -318,6 +323,9 @@ private class ProfileInsertImpl( } } + override fun include(fields: Fields.() -> Sequence) = + include((fields(Fields))) + override fun rawContact(configureRawContact: NewRawContact.() -> Unit): ProfileInsert = rawContact(NewRawContact().apply(configureRawContact)) diff --git a/core/src/main/java/contacts/core/profile/ProfileQuery.kt b/core/src/main/java/contacts/core/profile/ProfileQuery.kt index 842e5287..317bd5c1 100644 --- a/core/src/main/java/contacts/core/profile/ProfileQuery.kt +++ b/core/src/main/java/contacts/core/profile/ProfileQuery.kt @@ -28,19 +28,15 @@ import contacts.core.util.unsafeLazy * * ## Usage * - * Here is an example query that returns the profile [Contact]. Only RawContacts belonging to the - * given account are included. Only the full name and email address attributes of the profile - * [Contact] are included. + * Here is an example query that returns the profile [Contact]. Include only RawContacts belonging + * to the given account. Include only the name and email properties of the profile [Contact]. * * In Kotlin, * * ```kotlin - * import contacts.core.Fields.Name - * import contacts.core.Fields.Address - * * val profileContact : Contact? = profileQuery. * .accounts(account) - * .include(Name, Address) + * .include { Name.all + Address.all } * .find() * ``` * @@ -51,7 +47,10 @@ import contacts.core.util.unsafeLazy * * List contacts = profileQuery * .accounts(account) - * .include(Name, Address) + * .include(new ArrayList<>() {{ + * addAll(Name.getAll()); + * addAll(Address.getAll()); + * }}) * .find(); * ``` */ @@ -159,6 +158,11 @@ interface ProfileQuery { */ fun include(fields: Sequence): ProfileQuery + /** + * See [ProfileQuery.include]. + */ + fun include(fields: Fields.() -> Collection): ProfileQuery + /** * Returns the profile [Contact], if available. * @@ -256,6 +260,9 @@ private class ProfileQueryImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun find(): Contact? = find { false } override fun find(cancel: () -> Boolean): Contact? { diff --git a/core/src/main/java/contacts/core/profile/ProfileUpdate.kt b/core/src/main/java/contacts/core/profile/ProfileUpdate.kt index 8cc57808..951c50bb 100644 --- a/core/src/main/java/contacts/core/profile/ProfileUpdate.kt +++ b/core/src/main/java/contacts/core/profile/ProfileUpdate.kt @@ -154,6 +154,11 @@ interface ProfileUpdate { */ fun include(fields: Sequence): ProfileUpdate + /** + * See [ProfileUpdate.include]. + */ + fun include(fields: Fields.() -> Collection): ProfileUpdate + /** * Adds the given [rawContacts] to the update queue, which will be updated on [commit]. */ @@ -275,6 +280,9 @@ private class ProfileUpdateImpl( } } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun rawContacts(vararg rawContacts: ExistingRawContactEntity) = rawContacts(rawContacts.asSequence()) diff --git a/core/src/main/java/contacts/core/util/ContactLinkResult.kt b/core/src/main/java/contacts/core/util/ContactLinkResult.kt index 7501cf89..1ff2936e 100644 --- a/core/src/main/java/contacts/core/util/ContactLinkResult.kt +++ b/core/src/main/java/contacts/core/util/ContactLinkResult.kt @@ -1,7 +1,6 @@ package contacts.core.util import contacts.core.Contacts -import contacts.core.Fields import contacts.core.`in` import contacts.core.entities.Contact import contacts.core.equalTo @@ -25,7 +24,7 @@ import contacts.core.equalTo fun ContactLinkResult.contact(contacts: Contacts, cancel: () -> Boolean = { false }): Contact? = contactId?.let { contacts.query() - .where(Fields.Contact.Id equalTo it) + .where { Contact.Id equalTo it } .find(cancel) .firstOrNull() } @@ -51,6 +50,6 @@ fun ContactUnlinkResult.contacts( ): List = if (rawContactIds.isEmpty()) { emptyList() } else { - contacts.query().where(Fields.RawContact.Id `in` rawContactIds).find(cancel) + contacts.query().where { RawContact.Id `in` rawContactIds }.find(cancel) } diff --git a/core/src/main/java/contacts/core/util/ContactRefresh.kt b/core/src/main/java/contacts/core/util/ContactRefresh.kt index 8d54cad6..25a69e10 100644 --- a/core/src/main/java/contacts/core/util/ContactRefresh.kt +++ b/core/src/main/java/contacts/core/util/ContactRefresh.kt @@ -2,7 +2,6 @@ package contacts.core.util import contacts.core.Contacts import contacts.core.ContactsException -import contacts.core.Fields import contacts.core.entities.Contact import contacts.core.entities.ExistingContactEntity import contacts.core.entities.MutableContact @@ -57,7 +56,7 @@ internal fun Contacts.findContactWithId( .find(cancel) } else { query() - .where(Fields.Contact.Id equalTo contactId) + .where { Contact.Id equalTo contactId } .find(cancel) .firstOrNull() } \ No newline at end of file diff --git a/core/src/main/java/contacts/core/util/FieldsWhere.kt b/core/src/main/java/contacts/core/util/FieldsWhere.kt new file mode 100644 index 00000000..521b32fd --- /dev/null +++ b/core/src/main/java/contacts/core/util/FieldsWhere.kt @@ -0,0 +1,570 @@ +@file:Suppress("FunctionName") + +package contacts.core.util + +import contacts.core.* + +// Provides a bunch of extensions for shortening construction of Where instances. +// AKA Kotlin DSL goodness! Yummy stuff! So delicious! Mouth-watering! + +/* + * ## DISCLAIMER! Elitist warning! + * + * Okay, look. Let me get real with you. Because I know someone will point this out. So, I'm + * writing this comment to say that "I know, but I'm still doing it this way". + * + * I know... that I could reduce all of the extensions defined in this file in two; + * + * 1. inline fun > S.where(where: S.() -> Where): Where = where(this) + * 2. inline fun T.where(where: T.() -> Where): Where = where(this) + * + * This would let consumers do stuff like, + * + * ``` + * .where{ Email.Address.where { startsWith("a") and endsWith("gmail.com") } } + * ``` + * + * However, do you see that small, but glaring imperfection? The ".where" is required because + * functions have to have names (extensions are no exception)! Consumers might as well just do... + * + * ``` + * .where{ Email.Address.run { startsWith("a") and endsWith("gmail.com") } } + * ``` + * + * It's the same thing! Those functions is just run with a different name! + * + * So, I'm choosing to write a bunch of extensions to get "sugar, spice, and everything nice"! + * + * We can probably use annotations and code-generation to generation these functions for us. + * However, I think it'd take more time writing the code generator than just writing these manually. + * + * ## Inline + * + * These syntactic sugar are unusable by Java users. Therefore, we should inline to not negatively + * impact runtime performance. + */ + +// region Data Table Fields + +// region AddressFields + +inline fun Fields.Address(where: AddressFields.() -> Where): Where = + where(Address) + +inline fun AddressFields.Type(where: AddressField.() -> Where): Where = + where(Type) + +inline fun AddressFields.Label(where: AddressField.() -> Where): Where = + where(Label) + +inline fun AddressFields.FormattedAddress( + where: AddressField.() -> Where +): Where = where(FormattedAddress) + +inline fun AddressFields.Street(where: AddressField.() -> Where): Where = + where(Street) + +inline fun AddressFields.PoBox(where: AddressField.() -> Where): Where = + where(PoBox) + +inline fun AddressFields.Neighborhood( + where: AddressField.() -> Where +): Where = where(Neighborhood) + +inline fun AddressFields.City(where: AddressField.() -> Where): Where = + where(City) + +inline fun AddressFields.Region( + where: AddressField.() -> Where +): Where = where(Region) + +inline fun AddressFields.PostCode( + where: AddressField.() -> Where +): Where = where(PostCode) + +inline fun AddressFields.Country( + where: AddressField.() -> Where +): Where = where(Country) + +// endregion + +// region DataContactsFields + +inline fun Fields.Contact( + where: DataContactsFields.() -> Where +): Where = where(Contact) + +inline fun DataContactsFields.Id( + where: DataContactsField.() -> Where +): Where = where(Id) + +inline fun DataContactsFields.DisplayNamePrimary( + where: DataContactsField.() -> Where +): Where = where(DisplayNamePrimary) + +inline fun DataContactsFields.DisplayNameAlt( + where: DataContactsField.() -> Where +): Where = where(DisplayNameAlt) + +inline fun DataContactsFields.LastUpdatedTimestamp( + where: DataContactsField.() -> Where +): Where = where(LastUpdatedTimestamp) + +// region DataContactsOptionsFields + +inline fun DataContactsFields.Options( + where: DataContactsOptionsFields.() -> Where +): Where = where(Options) + +internal inline fun DataContactsOptionsFields.Id( + where: DataContactsField.() -> Where +): Where = where(Id) + +inline fun DataContactsOptionsFields.Starred( + where: DataContactsField.() -> Where +): Where = where(Starred) + +inline fun DataContactsOptionsFields.CustomRingtone( + where: DataContactsField.() -> Where +): Where = where(CustomRingtone) + +inline fun DataContactsOptionsFields.SendToVoicemail( + where: DataContactsField.() -> Where +): Where = where(SendToVoicemail) + +// endregion + +inline fun DataContactsFields.PhotoUri( + where: DataContactsField.() -> Where +): Where = where(PhotoUri) + +inline fun DataContactsFields.PhotoThumbnailUri( + where: DataContactsField.() -> Where +): Where = where(PhotoThumbnailUri) + +inline fun DataContactsFields.HasPhoneNumber( + where: DataContactsField.() -> Where +): Where = where(HasPhoneNumber) + +// endregion + +inline fun Fields.DataId( + where: GenericDataField.() -> Where +): Where = where(DataId) + +// region EmailFields + +inline fun Fields.Email(where: EmailFields.() -> Where): Where = + where(Email) + +inline fun EmailFields.Type(where: EmailField.() -> Where): Where = + where(Type) + +inline fun EmailFields.Label(where: EmailField.() -> Where): Where = + where(Label) + +inline fun EmailFields.Address(where: EmailField.() -> Where): Where = + where(Address) + +// endregion + +// region EventFields + +inline fun Fields.Event(where: EventFields.() -> Where): Where = + where(Event) + +inline fun EventFields.Type(where: EventField.() -> Where): Where = + where(Type) + +inline fun EventFields.Label(where: EventField.() -> Where): Where = + where(Label) + +inline fun EventFields.Date(where: EventField.() -> Where): Where = + where(Date) + +// endregion + +// region GroupMembershipFields + +inline fun Fields.GroupMembership( + where: GroupMembershipFields.() -> Where +): Where = where(GroupMembership) + +inline fun GroupMembershipFields.GroupId( + where: GroupMembershipField.() -> Where +): Where = where(GroupId) + +// endregion + +// region ImFields + +inline fun Fields.Im(where: ImFields.() -> Where): Where = + where(Im) + +inline fun ImFields.Protocol(where: ImField.() -> Where): Where = + where(Protocol) + +inline fun ImFields.CustomProtocol(where: ImField.() -> Where): Where = + where(CustomProtocol) + +inline fun ImFields.Data(where: ImField.() -> Where): Where = + where(Data) + +// endregion + +inline fun Fields.IsPrimary( + where: GenericDataField.() -> Where +): Where = where(IsPrimary) + +inline fun Fields.IsSuperPrimary( + where: GenericDataField.() -> Where +): Where = where(IsSuperPrimary) + +internal inline fun Fields.MimeType( + where: GenericDataField.() -> Where +): Where = where(MimeType) + +// region NameFields + +inline fun Fields.Name(where: NameFields.() -> Where): Where = + where(Name) + +inline fun NameFields.DisplayName(where: NameField.() -> Where): Where = + where(DisplayName) + +inline fun NameFields.GivenName(where: NameField.() -> Where): Where = + where(GivenName) + +inline fun NameFields.MiddleName(where: NameField.() -> Where): Where = + where(MiddleName) + +inline fun NameFields.FamilyName(where: NameField.() -> Where): Where = + where(FamilyName) + +inline fun NameFields.Prefix(where: NameField.() -> Where): Where = + where(Prefix) + +inline fun NameFields.Suffix(where: NameField.() -> Where): Where = + where(Suffix) + +inline fun NameFields.PhoneticGivenName(where: NameField.() -> Where): Where = + where(PhoneticGivenName) + +inline fun NameFields.PhoneticMiddleName( + where: NameField.() -> Where +): Where = where(PhoneticMiddleName) + +inline fun NameFields.PhoneticFamilyName( + where: NameField.() -> Where +): Where = where(PhoneticFamilyName) + +// endregion + +// region NicknameFields + +inline fun Fields.Nickname(where: NicknameFields.() -> Where): Where = + where(Nickname) + +inline fun NicknameFields.Name(where: NicknameField.() -> Where): Where = + where(Name) + +// endregion + +// region NoteFields + +inline fun Fields.Note(where: NoteFields.() -> Where): Where = + where(Note) + +inline fun NoteFields.Note(where: NoteField.() -> Where): Where = + where(Note) + +// endregion + +// region OrganizationFields + +inline fun Fields.Organization( + where: OrganizationFields.() -> Where +): Where = where(Organization) + +inline fun OrganizationFields.Company( + where: OrganizationField.() -> Where +): Where = where(Company) + +inline fun OrganizationFields.Title( + where: OrganizationField.() -> Where +): Where = where(Title) + +inline fun OrganizationFields.Department( + where: OrganizationField.() -> Where +): Where = where(Department) + +inline fun OrganizationFields.JobDescription( + where: OrganizationField.() -> Where +): Where = where(JobDescription) + +inline fun OrganizationFields.OfficeLocation( + where: OrganizationField.() -> Where +): Where = where(OfficeLocation) + +inline fun OrganizationFields.Symbol( + where: OrganizationField.() -> Where +): Where = where(Symbol) + +inline fun OrganizationFields.PhoneticName( + where: OrganizationField.() -> Where +): Where = where(PhoneticName) + +// endregion + +// region PhoneFields + +inline fun Fields.Phone(where: PhoneFields.() -> Where): Where = + where(Phone) + +inline fun PhoneFields.Type(where: PhoneField.() -> Where): Where = + where(Type) + +inline fun PhoneFields.Label(where: PhoneField.() -> Where): Where = + where(Label) + +inline fun PhoneFields.Number(where: PhoneField.() -> Where): Where = + where(Number) + +inline fun PhoneFields.NormalizedNumber(where: PhoneField.() -> Where): Where = + where(NormalizedNumber) + +// endregion + +// region PhotoFields + +inline fun Fields.Photo(where: PhotoFields.() -> Where): Where = + where(Photo) + +internal inline fun PhotoFields.PhotoFileId(where: PhotoField.() -> Where): Where = + where(PhotoFileId) + +internal inline fun PhotoFields.PhotoThumbnail(where: PhotoField.() -> Where): Where = + where(PhotoThumbnail) + +// endregion + +// region DataRawContactsFields + +inline fun Fields.RawContact( + where: DataRawContactsFields.() -> Where +): Where = where(RawContact) + +inline fun DataRawContactsFields.Id( + where: DataRawContactsField.() -> Where +): Where = where(Id) + + +// endregion + +// region RelationFields + +inline fun Fields.Relation(where: RelationFields.() -> Where): Where = + where(Relation) + +inline fun RelationFields.Type(where: RelationField.() -> Where): Where = + where(Type) + +inline fun RelationFields.Label(where: RelationField.() -> Where): Where = + where(Label) + +inline fun RelationFields.Name(where: RelationField.() -> Where): Where = + where(Name) + +// endregion + +// region SipAddressFields + +inline fun Fields.SipAddress( + where: SipAddressFields.() -> Where +): Where = where(SipAddress) + +inline fun SipAddressFields.SipAddress( + where: SipAddressField.() -> Where +): Where = where(SipAddress) + +// endregion + +// region WebsiteFields + +inline fun Fields.Website( + where: WebsiteFields.() -> Where +): Where = where(Website) + +inline fun WebsiteFields.Url( + where: WebsiteField.() -> Where +): Where = where(Url) + +// endregion + +// endregion + +// region AggregationExceptions Table Fields + +internal inline fun AggregationExceptionsFields.Type( + where: AggregationExceptionsField.() -> Where +): Where = where(Type) + +internal inline fun AggregationExceptionsFields.RawContactId1( + where: AggregationExceptionsField.() -> Where +): Where = where(RawContactId1) + +internal inline fun AggregationExceptionsFields.RawContactId2( + where: AggregationExceptionsField.() -> Where +): Where = where(RawContactId2) + +// endregion + +// region Contacts Table Fields + +inline fun ContactsFields.Id( + where: ContactsField.() -> Where +): Where = where(Id) + +inline fun ContactsFields.DisplayNamePrimary( + where: ContactsField.() -> Where +): Where = where(DisplayNamePrimary) + +inline fun ContactsFields.DisplayNameAlt( + where: ContactsField.() -> Where +): Where = where(DisplayNameAlt) + +inline fun ContactsFields.LastUpdatedTimestamp( + where: ContactsField.() -> Where +): Where = where(LastUpdatedTimestamp) + +// region ContactsOptionsFields + +inline fun ContactsFields.Options( + where: ContactsOptionsFields.() -> Where +): Where = where(Options) + +internal inline fun ContactsOptionsFields.Id( + where: ContactsField.() -> Where +): Where = where(Id) + +inline fun ContactsOptionsFields.Starred( + where: ContactsField.() -> Where +): Where = where(Starred) + +inline fun ContactsOptionsFields.CustomRingtone( + where: ContactsField.() -> Where +): Where = where(CustomRingtone) + +inline fun ContactsOptionsFields.SendToVoicemail( + where: ContactsField.() -> Where +): Where = where(SendToVoicemail) + +// endregion + +internal inline fun ContactsFields.DisplayNameSource( + where: ContactsField.() -> Where +): Where = where(DisplayNameSource) + +internal inline fun ContactsFields.NameRawContactId( + where: ContactsField.() -> Where +): Where = where(NameRawContactId) + +inline fun ContactsFields.PhotoUri( + where: ContactsField.() -> Where +): Where = where(PhotoUri) + +inline fun ContactsFields.PhotoThumbnailUri( + where: ContactsField.() -> Where +): Where = where(PhotoThumbnailUri) + +internal inline fun ContactsFields.PhotoFileId( + where: ContactsField.() -> Where +): Where = where(PhotoFileId) + +inline fun ContactsFields.HasPhoneNumber( + where: ContactsField.() -> Where +): Where = where(HasPhoneNumber) + +// endregion + +// region Groups Table Fields + +inline fun GroupsFields.Id(where: GroupsField.() -> Where): Where = + where(Id) + +inline fun GroupsFields.SystemId(where: GroupsField.() -> Where): Where = + where(SystemId) + +inline fun GroupsFields.Title(where: GroupsField.() -> Where): Where = + where(Title) + +inline fun GroupsFields.ReadOnly(where: GroupsField.() -> Where): Where = + where(ReadOnly) + +inline fun GroupsFields.Favorites(where: GroupsField.() -> Where): Where = + where(Favorites) + +inline fun GroupsFields.AutoAdd(where: GroupsField.() -> Where): Where = + where(AutoAdd) + +inline fun GroupsFields.AccountName( + where: GroupsField.() -> Where +): Where = where(Id) + +inline fun GroupsFields.AccountType( + where: GroupsField.() -> Where +): Where = where(AccountType) + +// endregion + +// region RawContacts Table Fields + +inline fun RawContactsFields.Id( + where: RawContactsField.() -> Where +): Where = where(Id) + +inline fun RawContactsFields.ContactId( + where: RawContactsField.() -> Where +): Where = where(ContactId) + +inline fun RawContactsFields.DisplayNamePrimary( + where: RawContactsField.() -> Where +): Where = where(DisplayNamePrimary) + +inline fun RawContactsFields.DisplayNameAlt( + where: RawContactsField.() -> Where +): Where = where(DisplayNameAlt) + +inline fun RawContactsFields.AccountName( + where: RawContactsField.() -> Where +): Where = where(AccountName) + +inline fun RawContactsFields.AccountType( + where: RawContactsField.() -> Where +): Where = where(AccountType) + +// region RawContactsOptionsFields + +inline fun RawContactsFields.Options( + where: RawContactsOptionsFields.() -> Where +): Where = where(Options) + +internal inline fun RawContactsOptionsFields.Id( + where: RawContactsField.() -> Where +): Where = where(Id) + +inline fun RawContactsOptionsFields.Starred( + where: RawContactsField.() -> Where +): Where = where(Starred) + +inline fun RawContactsOptionsFields.CustomRingtone( + where: RawContactsField.() -> Where +): Where = where(CustomRingtone) + +inline fun RawContactsOptionsFields.SendToVoicemail( + where: RawContactsField.() -> Where +): Where = where(SendToVoicemail) + +// endregion + +// endregion \ No newline at end of file diff --git a/core/src/main/java/contacts/core/util/GroupMembershipGroup.kt b/core/src/main/java/contacts/core/util/GroupMembershipGroup.kt index 76b60377..e0055567 100644 --- a/core/src/main/java/contacts/core/util/GroupMembershipGroup.kt +++ b/core/src/main/java/contacts/core/util/GroupMembershipGroup.kt @@ -27,7 +27,7 @@ import contacts.core.equalTo @JvmOverloads fun GroupMembershipEntity.group(contacts: Contacts, cancel: () -> Boolean = { false }): Group? = groupId?.let { - contacts.groups().query().where(GroupsFields.Id equalTo it).find(cancel).first() + contacts.groups().query().where { Id equalTo it }.find(cancel).first() } /** @@ -53,7 +53,7 @@ fun Collection.groups( emptyList() } else { contacts.groups().query() - .where(GroupsFields.Id `in` membershipIds) + .where { Id `in` membershipIds } .find(cancel) } } \ No newline at end of file diff --git a/core/src/main/java/contacts/core/util/GroupsInsertResult.kt b/core/src/main/java/contacts/core/util/GroupsInsertResult.kt index 885f08c1..86202a63 100644 --- a/core/src/main/java/contacts/core/util/GroupsInsertResult.kt +++ b/core/src/main/java/contacts/core/util/GroupsInsertResult.kt @@ -1,7 +1,6 @@ package contacts.core.util import contacts.core.Contacts -import contacts.core.GroupsFields import contacts.core.`in` import contacts.core.entities.Group import contacts.core.entities.NewGroup @@ -27,7 +26,7 @@ fun GroupsInsert.Result.group( contacts: Contacts, group: NewGroup, cancel: () -> Boolean = { false } ): Group? = groupId(group)?.let { groupId -> contacts.groups().query() - .where(GroupsFields.Id equalTo groupId) + .where { Id equalTo groupId } .find(cancel) .firstOrNull() } @@ -52,6 +51,6 @@ fun GroupsInsert.Result.groups(contacts: Contacts, cancel: () -> Boolean = { fal emptyList() } else { contacts.groups().query() - .where(GroupsFields.Id `in` groupIds) + .where { Id `in` groupIds } .find(cancel) } \ No newline at end of file diff --git a/core/src/main/java/contacts/core/util/InsertResult.kt b/core/src/main/java/contacts/core/util/InsertResult.kt index 6d09bfa0..f1435990 100644 --- a/core/src/main/java/contacts/core/util/InsertResult.kt +++ b/core/src/main/java/contacts/core/util/InsertResult.kt @@ -1,9 +1,12 @@ package contacts.core.util -import contacts.core.* +import contacts.core.Contacts +import contacts.core.Insert +import contacts.core.`in` import contacts.core.entities.Contact import contacts.core.entities.NewRawContact import contacts.core.entities.RawContact +import contacts.core.equalTo /** * Returns the newly created [RawContact] or null if the insert operation failed. @@ -30,7 +33,7 @@ fun Insert.Result.rawContact( val rawContactId = rawContactId(rawContact) ?: return null return contacts.query() - .where(Fields.RawContact.Id equalTo rawContactId) + .where { RawContact.Id equalTo rawContactId } .find(cancel) .asSequence() .flatMap { it.rawContacts.asSequence() } @@ -57,7 +60,7 @@ fun Insert.Result.rawContacts( contacts: Contacts, cancel: () -> Boolean = { false } ): List = - contacts.query().where(Fields.RawContact.Id `in` rawContactIds) + contacts.query().where { RawContact.Id `in` rawContactIds } .find(cancel) .asSequence() .flatMap { it.rawContacts.asSequence() } @@ -90,7 +93,7 @@ fun Insert.Result.contact( val rawContactId = rawContactId(rawContact) ?: return null return contacts.query() - .where(Fields.RawContact.Id equalTo rawContactId) + .where { RawContact.Id equalTo rawContactId } .find(cancel) .firstOrNull() } @@ -119,5 +122,5 @@ fun Insert.Result.contacts( cancel: () -> Boolean = { false } ): List = contacts.query() - .where(Fields.RawContact.Id `in` rawContactIds) + .where { RawContact.Id `in` rawContactIds } .find(cancel) \ No newline at end of file diff --git a/core/src/main/java/contacts/core/util/RawContactRefresh.kt b/core/src/main/java/contacts/core/util/RawContactRefresh.kt index da7672a2..75ad7dd3 100644 --- a/core/src/main/java/contacts/core/util/RawContactRefresh.kt +++ b/core/src/main/java/contacts/core/util/RawContactRefresh.kt @@ -2,7 +2,6 @@ package contacts.core.util import contacts.core.Contacts import contacts.core.ContactsException -import contacts.core.Fields import contacts.core.entities.ExistingRawContactEntity import contacts.core.entities.MutableRawContact import contacts.core.entities.RawContact @@ -54,7 +53,7 @@ internal fun Contacts.findRawContactWithId( .find(cancel) } else { query() - .where(Fields.RawContact.Id equalTo rawContactId) + .where { RawContact.Id equalTo rawContactId } .find(cancel) .firstOrNull() } diff --git a/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderDataQuery.kt b/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderDataQuery.kt index e534347f..dae34144 100644 --- a/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderDataQuery.kt +++ b/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderDataQuery.kt @@ -6,4 +6,5 @@ import contacts.core.data.DataQueryFactory /** * Queries for [Gender]s. */ -fun DataQueryFactory.genders(): DataQuery = customData(GenderMimeType) \ No newline at end of file +fun DataQueryFactory.genders(): DataQuery = + customData(GenderMimeType) \ No newline at end of file diff --git a/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderFields.kt b/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderFields.kt index beb537c1..53dc64d7 100644 --- a/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderFields.kt +++ b/customdata-gender/src/main/java/contacts/entities/custom/gender/GenderFields.kt @@ -3,6 +3,7 @@ package contacts.entities.custom.gender import contacts.core.AbstractCustomDataField import contacts.core.AbstractCustomDataField.ColumnName import contacts.core.AbstractCustomDataFieldSet +import contacts.core.Where import contacts.core.entities.MimeType data class GenderField internal constructor(private val columnName: ColumnName) : @@ -41,4 +42,12 @@ object GenderFields : AbstractCustomDataFieldSet() { */ @JvmStatic fun forMatching() = forMatching -} \ No newline at end of file +} + +@Suppress("FunctionName") +inline fun GenderFields.Type(where: GenderField.() -> Where): Where = + where(Type) + +@Suppress("FunctionName") +inline fun GenderFields.Label(where: GenderField.() -> Where): Where = + where(Label) \ No newline at end of file diff --git a/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameDataQuery.kt b/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameDataQuery.kt index 1b3120f5..9d9e2710 100644 --- a/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameDataQuery.kt +++ b/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameDataQuery.kt @@ -6,5 +6,5 @@ import contacts.core.data.DataQueryFactory /** * Queries for [HandleName]s. */ -fun DataQueryFactory.handleNames(): DataQuery = +fun DataQueryFactory.handleNames(): DataQuery = customData(HandleNameMimeType) \ No newline at end of file diff --git a/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameFields.kt b/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameFields.kt index 5badc531..071a363f 100644 --- a/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameFields.kt +++ b/customdata-handlename/src/main/java/contacts/entities/custom/handlename/HandleNameFields.kt @@ -3,6 +3,7 @@ package contacts.entities.custom.handlename import contacts.core.AbstractCustomDataField import contacts.core.AbstractCustomDataField.ColumnName import contacts.core.AbstractCustomDataFieldSet +import contacts.core.Where import contacts.core.entities.MimeType data class HandleNameField internal constructor(private val columnName: ColumnName) : @@ -38,4 +39,9 @@ object HandleNameFields : AbstractCustomDataFieldSet() { */ @JvmStatic fun forMatching() = forMatching -} \ No newline at end of file +} + +@Suppress("FunctionName") +inline fun HandleNameFields.Handle( + where: HandleNameField.() -> Where +): Where = where(Handle) \ No newline at end of file diff --git a/howto/howto-include-only-desired-data.md b/howto/howto-include-only-desired-data.md index cc15ab12..31c75279 100644 --- a/howto/howto-include-only-desired-data.md +++ b/howto/howto-include-only-desired-data.md @@ -13,7 +13,7 @@ Each field corresponds with an Entity property. For example, to include only the display name, organization company, and all phone number fields, ```kotlin -query.include(mutableListOf().apply { +query.include(mutableSetOf().apply { add(Fields.Contact.DisplayNamePrimary) add(Fields.Organization.Company) addAll(Fields.Phone.all) diff --git a/howto/howto-insert-contacts.md b/howto/howto-insert-contacts.md index 67fff236..c3f4750c 100644 --- a/howto/howto-insert-contacts.md +++ b/howto/howto-insert-contacts.md @@ -151,10 +151,10 @@ To include only the given set of fields (data) in each of the insert operation, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, @@ -212,7 +212,7 @@ Once you have the RawContact IDs, you can retrieve the newly created Contacts vi ```kotlin val contacts = contactsApi .query() - .where(Fields.RawContact.Id `in` allRawContactIds) + .where { RawContact.Id `in` allRawContactIds } .find() ``` @@ -375,10 +375,10 @@ val insertResult = Contacts(context) .groups() .query() .accounts(accountToAddContactTo) - .where( - (GroupsFields.Favorites equalTo true) or - (GroupsFields.Title contains "friend") - ) + .where { + (Favorites equalTo true) or + (Title contains "friend") + } .find() .newMemberships() ) diff --git a/howto/howto-insert-groups.md b/howto/howto-insert-groups.md index 6a6a5389..e541ad67 100644 --- a/howto/howto-insert-groups.md +++ b/howto/howto-insert-groups.md @@ -106,7 +106,7 @@ Once you have the Group IDs, you can retrieve the newly created Groups via the ` val groups = contactsApi .groups() .query() - .where(GroupsFields.Id `in` allGroupIds) + .where { Id `in` allGroupIds } .find() ``` diff --git a/howto/howto-insert-profile.md b/howto/howto-insert-profile.md index d2163843..5aca98b8 100644 --- a/howto/howto-insert-profile.md +++ b/howto/howto-insert-profile.md @@ -169,10 +169,10 @@ To include only the given set of fields (data) in each of the insert operation, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) @@ -217,7 +217,7 @@ Once you have the RawContact ID, you can retrieve the newly created Contact via ```kotlin val contacts = contactsApi .query() - .where(Fields.RawContact.Id equalTo rawContactId) + .where { RawContact.Id equalTo rawContactId } .find() ``` diff --git a/howto/howto-query-contacts-advanced.md b/howto/howto-query-contacts-advanced.md index 978c3cda..6fb79500 100644 --- a/howto/howto-query-contacts-advanced.md +++ b/howto/howto-query-contacts-advanced.md @@ -38,25 +38,25 @@ the results) ordered by display name in descending order, matching ALL of these ```kotlin val contacts = Contacts(context) .query() - .where( - (Fields.Name.GivenName startsWith "leo") and - ((Fields.Email.Address endsWith "gmail.com") or (Fields.Email.Address endsWith "hotmail.com")) and - (Fields.Address.Country equalToIgnoreCase "us") and - ((Fields.Event.Date lessThan Date().toWhereString()) and (Fields.Event.Type equalTo EventEntity.Type.BIRTHDAY)) and - (Fields.Contact.Options.Starred equalTo true) and - (Fields.Nickname.Name equalTo "DarEdEvil") and - (Fields.Organization.Company `in` listOf("facebook", "FB")) and - (Fields.Note.Note.isNotNullOrEmpty()) - ) + .where { + (Name.GivenName startsWith "leo") and + (Email.Address { endsWith("gmail.com") or endsWith("hotmail.com") }) and + (Address.Country equalToIgnoreCase "us") and + (Event { (Date lessThan Date().toWhereString()) and (Type equalTo EventEntity.Type.BIRTHDAY) }) and + (Contact.Options.Starred equalTo true) and + (Nickname.Name equalTo "DarEdEvil") and + (Organization.Company `in` listOf("facebook", "FB")) and + (Note.Note.isNotNullOrEmpty()) + } .accounts( Account("john.doe@gmail.com", "com.google"), Account("john.doe@myspace.com", "com.myspace"), ) - .include( - Fields.Contact.Id, - Fields.Contact.DisplayNamePrimary, - Fields.Phone.Number - ) + .include { setOf( + Contact.Id, + Contact.DisplayNamePrimary, + Phone.Number + ) } .orderBy(ContactsFields.DisplayNamePrimary.desc()) .offset(0) .limit(5) @@ -82,7 +82,7 @@ To get all contacts with a phone number AND email, val contacts = Contacts(context) .query() ... - .where(Fields.Phone.Number.isNotNullOrEmpty() and Fields.Email.Address.isNotNullOrEmpty()) + .where{ Phone.Number.isNotNullOrEmpty() and Email.Address.isNotNullOrEmpty() } .find() ``` @@ -92,7 +92,7 @@ To get a list of contacts with the given IDs, val contacts = Contacts(context) .query() ... - .where(Fields.Contact.Id `in` contactIds) + .where { Contact.Id `in` contactIds } .find() ``` @@ -147,10 +147,10 @@ To include only the given set of fields (data) in each of the matching contacts, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) @@ -160,7 +160,7 @@ For more info, read [How do I include only the data that I want?](/howto/howto-i To limit the search to only those RawContacts associated with at least one of the given groups, ```kotlin -.where(Fields.GroupMembership.GroupId `in` groups.mapNotNull { it.id }) +.where { GroupMembership.GroupId `in` groups.mapNotNull { it.id } } ``` > For more info, read [How do I retrieve groups?](/howto/howto-query-groups.md) @@ -299,7 +299,7 @@ For example, to get all contacts with a phone number AND email, val contacts = Contacts(context) .query() ... - .where(Fields.Phone.Number.isNotNullOrEmpty() and Fields.Email.Address.isNotNullOrEmpty()) + .where { Phone.Number.isNotNullOrEmpty() and Email.Address.isNotNullOrEmpty() } .find() ``` @@ -309,7 +309,7 @@ To get a list of contacts with the given IDs, val contacts = Contacts(context) .query() ... - .where(Fields.Contact.Id `in` contactIds) + .where { Contact.Id `in` contactIds } .find() ``` @@ -335,12 +335,12 @@ that were not part of the first query results. For example, ```kotlin val contactsWithEmails = query .include(Fields.Contact.Id) - .where(Fields.Email.Address.isNotNullOrEmpty()) + .where { Email.Address.isNotNullOrEmpty() } .find() val contactIdsWithEmails = contactsWithEmails.mapNotNull { it.id } val contactsWithoutEmails = query - .where(Fields.Contact.Id notIn contactIdsWithEmails) + .where { Contact.Id notIn contactIdsWithEmails } .find() ``` @@ -355,7 +355,7 @@ queries. For example, ```kotlin val contactsWithNoPhoneNumbers = query - .where(Fields.Contact.HasPhoneNumber notEqualTo true) + .where { Contact.HasPhoneNumber notEqualTo true } .find() ``` diff --git a/howto/howto-query-contacts.md b/howto/howto-query-contacts.md index 41f85d1c..3d350a70 100644 --- a/howto/howto-query-contacts.md +++ b/howto/howto-query-contacts.md @@ -118,10 +118,10 @@ To include only the given set of fields (data) in each of the matching contacts, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) diff --git a/howto/howto-query-custom-data.md b/howto/howto-query-custom-data.md index fe057ccf..ead36dd1 100644 --- a/howto/howto-query-custom-data.md +++ b/howto/howto-query-custom-data.md @@ -58,7 +58,7 @@ val handleNames = Contacts(context) .data() .query() .handleNames() - .where(HandleNameFields.Handle startsWith "h") + .where { Handle startsWith "h" } .find() ``` @@ -72,10 +72,10 @@ that can be used in this function. By default, not calling the `include` function will include all fields, including custom data fields. -For example, to specifically include only `HandleName` and `Gender` fields, +For example, to explicitly include all `HandleName` fields, ```kotlin -.include(HandleNameFields.all + GenderFields.all) +.include(HandleNameFields.all) ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) @@ -87,7 +87,7 @@ criteria based on specific field values. Custom data entries provides fields tha this function. For example, to match `HandleName`s starting with the letter "h", ```kotlin -.where(HandleNameFields.Handle startsWith "h") +.where { Handle startsWith "h" } ``` The `BroadQuery` API provides a `whereAnyContactDataPartiallyMatches` function that NOT support diff --git a/howto/howto-query-data-sets.md b/howto/howto-query-data-sets.md index 8e6b2c27..0331c65e 100644 --- a/howto/howto-query-data-sets.md +++ b/howto/howto-query-data-sets.md @@ -54,10 +54,7 @@ val websites = Contacts(this) .data() .query() .websites() - .where( - (Fields.Website.Url endsWith ".net") - and (Fields.Contact.Id `in` contactIds) - ) + .where { (Website.Url endsWith ".net") and (Contact.Id `in` contactIds) } .find() ``` @@ -240,7 +237,7 @@ val birthdayEvents = Contacts(this) .data() .query() .events() - .where(Fields.Event.Type equalTo EventEntity.Type.BIRTHDAY) + .where { Event.Type equalTo EventEntity.Type.BIRTHDAY } .find() ``` diff --git a/howto/howto-query-groups.md b/howto/howto-query-groups.md index 17256f8f..365ef981 100644 --- a/howto/howto-query-groups.md +++ b/howto/howto-query-groups.md @@ -151,13 +151,13 @@ WHERE clauses. For example, to find groups with a specific title, ```kotlin -.where(GroupsFields.Title equalToIgnoreCase "friends") +.where { Title equalToIgnoreCase "friends" } ``` To get a list of groups by IDs, ```kotlin -.where(GroupsFields.Id `in` groupIds) +.where { Id `in` groupIds } ``` ## Different groups with the same titles diff --git a/howto/howto-query-profile.md b/howto/howto-query-profile.md index c60a421d..3a350e35 100644 --- a/howto/howto-query-profile.md +++ b/howto/howto-query-profile.md @@ -70,10 +70,10 @@ To include only the given set of fields (data) in each of the Profile Contact, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) diff --git a/howto/howto-query-raw-contacts.md b/howto/howto-query-raw-contacts.md index d88462f4..753e011a 100644 --- a/howto/howto-query-raw-contacts.md +++ b/howto/howto-query-raw-contacts.md @@ -167,7 +167,7 @@ If you want to get the Contacts and all associated RawContacts and Data from a s ```kotlin val contacts = Contacts(context) .query() - .where(Fields.RawContact.Id `in` blankRawContactIds) + .where { RawContact.Id `in` blankRawContactIds } .find() ``` @@ -201,7 +201,7 @@ For example, to get all favorite RawContacts, val favoriteRawContacts = Contacts(context) .accounts() .queryRawContacts() - .where(RawContactsFields.Options.Starred equalTo true) + .where { Options.Starred equalTo true } .find() ``` @@ -211,7 +211,7 @@ To get a list of RawContacts with the given IDs, val favoriteRawContacts = Contacts(context) .accounts() .queryRawContacts() - .where(RawContactsFields.Id `in` rawContactIds) + .where { Id `in` rawContactIds } .find() ``` diff --git a/howto/howto-update-contacts.md b/howto/howto-update-contacts.md index b82b7d19..fb5ab9c7 100644 --- a/howto/howto-update-contacts.md +++ b/howto/howto-update-contacts.md @@ -72,10 +72,10 @@ To include only the given set of fields (data) in each of the update operation, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) @@ -120,7 +120,7 @@ Once you have performed the updates, you can retrieve the updated Contacts refer ```kotlin val updatedContacts = contactsApi .query() - .where(Fields.Contact.Id `in` listOf(contact1.id)) + .where { Contact.Id `in` listOf(contact1.id) } .find() ``` diff --git a/howto/howto-update-data-sets.md b/howto/howto-update-data-sets.md index 5310d939..55b2eb5c 100644 --- a/howto/howto-update-data-sets.md +++ b/howto/howto-update-data-sets.md @@ -48,10 +48,10 @@ To include only the given set of fields (data) in each of the update operation, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) @@ -100,7 +100,7 @@ val updatedEmail = contactsApi .data() .query() .emails() - .where(Fields.Email.Id equalTo emailId) + .where { Email.Id equalTo emailId } .find() ``` diff --git a/howto/howto-update-profile.md b/howto/howto-update-profile.md index 79982ef8..0bc7098e 100644 --- a/howto/howto-update-profile.md +++ b/howto/howto-update-profile.md @@ -61,10 +61,10 @@ To include only the given set of fields (data) in each of the update operation, .include(fields) ``` -For example, to only include email fields, +For example, to only include email and name fields, ```kotlin -.include(Fields.Email.all) +.include { Email.all + Name.all } ``` For more info, read [How do I include only the data that I want?](/howto/howto-include-only-desired-data.md) diff --git a/sample/src/main/java/contacts/sample/view/ContactView.kt b/sample/src/main/java/contacts/sample/view/ContactView.kt index 3451cc6c..9495e73f 100644 --- a/sample/src/main/java/contacts/sample/view/ContactView.kt +++ b/sample/src/main/java/contacts/sample/view/ContactView.kt @@ -191,7 +191,7 @@ class ContactView @JvmOverloads constructor( */ suspend fun loadContactWithId(contactId: Long, contacts: Contacts): Boolean { val contact = contacts.queryWithPermission() - .where(Fields.Contact.Id equalTo contactId) + .where { Contact.Id equalTo contactId } .findWithContext() .firstOrNull() ?.mutableCopy() @@ -446,7 +446,7 @@ class ContactView @JvmOverloads constructor( // only to copy over the group memberships to the RawContacts =) val refreshedContact = contacts.queryWithPermission() .include(Fields.GroupMembership.all) - .where(Fields.Contact.Id equalTo contactId) + .where { Contact.Id equalTo contactId } .findWithContext() .firstOrNull() ?: return diff --git a/test/src/main/java/contacts/test/TestQuery.kt b/test/src/main/java/contacts/test/TestQuery.kt index 97b4d712..d75ceeb7 100644 --- a/test/src/main/java/contacts/test/TestQuery.kt +++ b/test/src/main/java/contacts/test/TestQuery.kt @@ -38,6 +38,9 @@ internal class TestQuery(private val query: Query) : Query { query.include(fields + TestDataFields.all) } + override fun include(fields: Fields.() -> Collection) = + include(fields(Fields)) + override fun where(where: Where?): TestQuery = apply { // Make sure only RawContacts with the test marker data are queried. query.where( @@ -49,6 +52,8 @@ internal class TestQuery(private val query: Query) : Query { ) } + override fun where(where: Fields.() -> Where?) = where(where(Fields)) + override fun orderBy(vararg orderBy: OrderBy): TestQuery = orderBy(orderBy.asSequence()) @@ -59,6 +64,9 @@ internal class TestQuery(private val query: Query) : Query { query.orderBy(orderBy) } + override fun orderBy(orderBy: ContactsFields.() -> Collection>) = + orderBy(orderBy(ContactsFields)) + override fun limit(limit: Int): TestQuery = apply { query.limit(limit) } diff --git a/test/src/main/java/contacts/test/entities/TestDataFields.kt b/test/src/main/java/contacts/test/entities/TestDataFields.kt index 3d3806a7..a66e05de 100644 --- a/test/src/main/java/contacts/test/entities/TestDataFields.kt +++ b/test/src/main/java/contacts/test/entities/TestDataFields.kt @@ -3,6 +3,7 @@ package contacts.test.entities import contacts.core.AbstractCustomDataField import contacts.core.AbstractCustomDataField.ColumnName import contacts.core.AbstractCustomDataFieldSet +import contacts.core.Where import contacts.core.entities.MimeType internal data class TestDataField internal constructor(private val columnName: ColumnName) : @@ -18,4 +19,9 @@ internal object TestDataFields : AbstractCustomDataFieldSet() { override val all: Set = setOf(Value) override val forMatching: Set = emptySet() -} \ No newline at end of file +} + +@Suppress("FunctionName") +internal inline fun TestDataFields.Handle( + where: TestDataField.() -> Where +): Where = where(Value) \ No newline at end of file