A modern contacts Android API.

Overview

Contact Store

Banner

Access to contacts is one of the most frequent use cases in Android applications. Even if your app is not a contact management app, there are various cases where you might need access to the device contacts (such as referring other users to the app).

For developers to access the device contacts, they need to use ContentProviders. This introduces a lot of frustrations and complications. For someone that has never worked with ContentProviders before, the documentation can be tedious to go through. The lack of a type-safe API leads to repeated errors, developer frustration, along with a waste of time and resources for the developer and the team.

Contact Store is a modern contacts Android API written in Kotlin. It utilises Coroutine's Flow to notify the developer for updates happening to the Contacts database.

Installation

Using Gradle:

repositories {
  ...
  mavenCentral()
}

dependencies {
    implementation 'com.alexstyl:contactstore:0.1.2'
}

How to fetch contacts using Contact Store?

The following sample returns a list of all contacts in the device. Each contact will contain an id, a display name and whether they are starred or not:

val store = ContactStore.newInstance(application)

store.fetchContacts()
    .collect { contacts ->
        val contactString = contacts.joinToString(", ") {
            "DisplayName = ${it.displayName}, isStarred = ${it.isStarred}, id = ${it.contactId}"
        }
        println("Contacts emitted: $contactString")
    }

⚠️ The user must have already granted the READ_CONTACTS permission before collecting the flow.

If you need to query specific details about a contact (commonly used in contact detail screens), the following sample returns a contact's Structured Names and phone numbers:

"Names = ${contact.firstName} ${contact.middleName} ${contact.lastName} " + "phones = ${phones.map { "${it.value} (${it.label})" }}" } println("Contacts emitted: $contactString") } } ">
val store = ContactStore.newInstance(application)

store.fetchContacts(
    predicate = ContactLookup(
        inContactIds = listOf(contactId)
    ),
    columnsToFetch = listOf(
        ContactColumn.NAMES,
        ContactColumn.PHONES
    )
)
    .collect { contacts ->
        val contact = contacts.firstOrNull()
        if (contact == null) {
            println("Contact not found")
        } else {
            val phones = contact.phones
            val contactString = contacts.joinToString(", ") { contact ->
                "Names = ${contact.firstName} ${contact.middleName} ${contact.lastName} " +
                        "phones = ${phones.map { "${it.value} (${it.label})" }}"
            }
            println("Contacts emitted: $contactString")
        }
    }

Always make sure to query only the columns that you need. In the case where a property is accessed which was not queried, an Exception is thrown.

How to edit contacts using Contact Store?

The following snippets show how to edit contacts in the device. ⚠️ The user must have already granted the WRITE_CONTACTS permission before calling execute().

Insert a new contact

val store = ContactStore.newInstance(application)

store.execute(SaveRequest().apply {
    insert(MutableContact().apply {
        firstName = "Paolo"
        lastName = "Melendez"
        phones.add(
            LabeledValue(
                value = PhoneNumber("555"),
                label = Label.PhoneNumberMobile
            )
        )
    })
})

Update an existing contact

In order to update a contact, you first need to get a reference to the contact from the store. Only the values queried will be updated. This is by design, in order to prevent accidental value overrides.

The following code modifies a contact's note:

val foundContacts = store.fetchContacts(
    predicate = ContactLookup(inContactIds = listOf(5L)),
    columnsToFetch = listOf(ContactColumn.NOTE)
).first()
if (foundContacts.isEmpty()) return // the contact was not found

val contact = foundContacts.first()

store.execute(SaveRequest().apply {
    update(contact.mutableCopy().apply {
        note = Note("To infinity and beyond!")
    })
})

Deleting a contact

The following code shows how to delete a contact by id:

store.execute(SaveRequest().apply {
    delete(contactId = 5L)
})

Getting Help

To report a specific problem or feature request, open a new issue on Github.

License

Apache 2.0. See the LICENSE file for details.

Author

Made by Alex Styl. Follow @alexstyl on Twitter for future updates.

Comments
  • URI formate exception

    URI formate exception

    Faced the following issue while using the library in prod, any possible solution for

    Fatal Exception: java.lang.IllegalArgumentException: uri format is unknown
           at com.alexstyl.contactstore.RawContactQueries.fetchRawContacts(:2)
           at com.alexstyl.contactstore.ContactQueries.access$fetchAdditionalColumns(:1)
           at com.alexstyl.contactstore.ContactQueries$queryContacts$$inlined$map$1$2.emit(:69)
           at com.alexstyl.contactstore.ContactQueries$queryAllContacts$$inlined$map$1$2.emit(:64)
           at com.alexstyl.contactstore.utils.ContentResolverKt$runQueryFlow$$inlined$map$1$2.emit(:74)
           at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
           at kotlinx.coroutines.flow.internal.SafeCollectorKt$emitFun$1.invoke(SafeCollector.kt:15)
           at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:87)
           at kotlinx.coroutines.flow.internal.SafeCollector.emit(SafeCollector.kt:66)
           at com.alexstyl.contactstore.utils.FlowExtensionsKt$a.invokeSuspend(:34)
           at com.alexstyl.contactstore.utils.FlowExtensionsKt$a.invoke(:1)
           at kotlinx.coroutines.flow.FlowKt__EmittersKt$onStart$$inlined$unsafeFlow$1.collect(SafeCollector.common.kt:116)
           at com.alexstyl.contactstore.utils.ContentResolverKt$runQueryFlow$$inlined$map$1.collect(:21)
           at com.alexstyl.contactstore.ContactQueries$queryAllContacts$$inlined$map$1.collect(:7)
           at com.alexstyl.contactstore.ContactQueries$queryContacts$$inlined$map$1.collect(:11)
           at kotlinx.coroutines.flow.FlowKt__ReduceKt.first(Reduce.kt:198)
           at kotlinx.coroutines.flow.FlowKt.first(:1)
           at com.alexstyl.contactstore.FetchRequest$a.invokeSuspend(:34)
           at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
           at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
           at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:279)
           at kotlinx.coroutines.BlockingCoroutine.joinBlocking(Builders.kt:85)
           at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking(Builders.kt:59)
           at kotlinx.coroutines.BuildersKt.runBlocking(:1)
           at kotlinx.coroutines.BuildersKt__BuildersKt.runBlocking$default(Builders.kt:38)
           at kotlinx.coroutines.BuildersKt.runBlocking$default(:1)
           at com.alexstyl.contactstore.FetchRequest.blockingGet(:7)
           at com.jobget.onboarding.requestendorsements.repo.DefaultUserContactsProvider.get$lambda-1(UserContactsProvider.kt:45)
           at com.jobget.onboarding.requestendorsements.repo.DefaultUserContactsProvider.$r8$lambda$PCXMhjAq1fthczh8YCeZcJhq6FM()
           at com.jobget.onboarding.requestendorsements.repo.DefaultUserContactsProvider$$ExternalSyntheticLambda4.call(:2)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableFromCallable.subscribeActual(ObservableFromCallable.java:46)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableMap.subscribeActual(ObservableMap.java:33)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableOnErrorReturn.subscribeActual(ObservableOnErrorReturn.java:31)
           at io.reactivex.rxjava3.core.Observable.subscribe(Observable.java:13176)
           at io.reactivex.rxjava3.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
           at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:644)
           at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
           at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
           at java.util.concurrent.FutureTask.run(FutureTask.java:266)
           at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
           at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
           at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
           at java.lang.Thread.run(Thread.java:923)
    

    We just fetch the contacts in our application to show them to our users.

    bug 
    opened by purna-jobget 11
  • Feature Request: Provide Paging-Support

    Feature Request: Provide Paging-Support

    When you have a lot of contacts, loading them all at once can take some time. For that reason, I would like to use paging. Therefore it would be really nice if a method could be provided which may look a bit like this:

        public fun fetchContactsPaged(
            predicate: ContactPredicate? = null,
            columnsToFetch: List<ContactColumn> = emptyList(),
            displayNameStyle: DisplayNameStyle = DisplayNameStyle.Primary,
            pageSize: Int,
            offset: Int,
        ): FetchRequest<List<Contact>>
    

    allowing the caller to pass a page size (how many records should be fetched) and an offset (assuming the contacts are sorted by displayName).

    opened by fgubler 9
  • Filter for duplicate numbers

    Filter for duplicate numbers

    Seems like the contact database might contain the same phone number multiple times.

    Someone shared a screenshot of the sample app, where the contact details show the same number multiple times. The contact had WhatsApp linked to it. An other contact contained the same phone three times. That contact had both WhatsApp and Signal installed.

    | Whatsapp | Whatsapp + Signal | | --- | --- | Frame 1 | Frame 2 |

    wontfix 
    opened by alexstyl 8
  • Feature request: Add sorting to the ContactStore api

    Feature request: Add sorting to the ContactStore api

    It would be really nice if users could sort the data by fields and ASC/DESC order. More specifically, support it on query level in content provider and not on the call site when the data is already fetched. Currently ContactQueries.kt use Contacts.DISPLAY_NAME_PRIMARYas a sort order by default in multiple lookups.

    I suggest adding the overloading method fetchContacts with the 3rd parameter sortOrder nullable by default, this will not break the existing code to the users and accept sorting order. Instead of String, there can be created some kind of data structure to predefined fields and ASC/DESC behavior. queryContacts function by default will accept Contacts.DISPLAY_NAME_PRIMARY as a parameter.

    opened by tatocaster 7
  • Feature Request: Allow passing multiple predicates

    Feature Request: Allow passing multiple predicates

    It would be great if there was a fetchContacts() method allowing the caller to pass multiple predicates. Or an additional predicate which searches over all fields of the contact.

    opened by fgubler 6
  • [Question] Creating new contacts

    [Question] Creating new contacts

    Sorry, this is more of a question than an issue but I was not sure about the ideal channel of communications and maybe the question is also interesting for other users of the library.

    As explained in the Readme, it is possible to create a new contact in the Android contact database, however for my real-world use-case I have two additional questions:

    1. I cant/don't have to pass a contactId along. Therefore, I assume that the ID will be assigned automatically: Is that correct (would make sense to me as Android is better able to guarantee its uniqueness).
    2. I have to pass the account into which the contact should be stored. Is there a way to get the information from Android (or ideally from this library), which accounts are available and which of them is the default?
    question 
    opened by fgubler 5
  • Refresh the sample app

    Refresh the sample app

    • updated dependencies
    • removed buildToolsVersion - the project automatically uses a default version of the build tools that the plugin specifies. docs
    • renamed compose function to follow compose naming convention
    • removed lint warnings - add an experimental annotation for Coil
    • sorted contacts in descending order by "isStarred" - to imitate the original Google contacts application
    • removed test runner - sample app does not have any tests
    opened by tatocaster 4
  • Introduce Github Actions

    Introduce Github Actions

    This change aims to introduce Github Action in the project - issue: #29

    Requirements:

    • [X] Have all unit tests and Android tests run when someone opens a PR.
    • [X] If any of the tests fail, then the PR should be blocked.
    • [X] Document is a way (i.e. on your PR) how this automation works.

    How it works?

    • Runs on every pull request on the target branch: "main"

    Steps are:

    • Checkout
    • Gradle wrapper validation - From the security perspective it is always good to check if the binary we download is trustful, in that case, it checks the checksum
    • Grants execution permission to Gradle
    • Runs all unit tests in all modules - ./gradlew test
    • Sets up an emulator with the API level 29 and runs all instrumented tests in all modules against the emulator - ./gradlew connectedAndroidtest

    How to set up branch protection and mergeability of PR?

    • Enable status checks in repository settings, Github docs with images.
    • Settings -> Actions tab -> "allow all actions". "Allow select actions" is much better but a bit hassle to update every time. Opt-in at your convenience.
    • Settings -> Actions tab -> "Read repository contents permission", feels safer. It will protect the project if some contributors accidentally misuse any step from the action.

    Future suggestions:

    • add lint checks
    • OWASP dependency check to check all the dependencies against the common vulnerabilities
    opened by tatocaster 3
  • Run unit tests and Android tests via Github Actions

    Run unit tests and Android tests via Github Actions

    There are more people contributing to the project lately, and it would be nice to have less moderation required on each PR. This issue is about running all unit tests and Android tests of all modules of the project, using Github Actions.

    Requirements:

    • Have all unit tests and Android tests to run when someone opens a PR.
    • If any of the tests fail, then the PR should be blocked.
    • Document is a way (i.e. on your PR) how this automation works.

    I have to admit that I have yet to experiment with Github Actions so I am not entirely sure what it fully entails.

    There is no need for this work to take place in a single PR.

    If you would like to pick up this task, reply to this issue, so that I can assign it to you.

    good first issue 
    opened by alexstyl 3
  • java.lang.IllegalStateException: Inline class has no underlying property name in metadata: deserialized class LookupKey

    java.lang.IllegalStateException: Inline class has no underlying property name in metadata: deserialized class LookupKey

    When someone tries to use the Contact#lookupKey property on ContactStore 0.7.0, they can see the following stacktrace in compilation time:

    Caused by: java.lang.IllegalStateException: Inline class has no underlying property name in metadata: deserialized class LookupKey
    	at org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor.computeInlineClassRepresentation(DeserializedClassDescriptor.kt:185)
    	at org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor.access$computeInlineClassRepresentation(DeserializedClassDescriptor.kt:33)
    	at org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor$inlineClassRepresentation$1.invoke(DeserializedClassDescriptor.kt:68)
    	at org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor$inlineClassRepresentation$1.invoke(DeserializedClassDescriptor.kt:68)
    	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:408)
    	at org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor.getInlineClassRepresentation(DeserializedClassDescriptor.kt:171)
    	at org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass$inlineClassRepresentation$2.invoke(IrLazyClass.kt:100)
    	at org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass$inlineClassRepresentation$2.invoke(IrLazyClass.kt:99)
    	at org.jetbrains.kotlin.ir.declarations.lazy.SynchronizedLazyVar.getValue(lazyUtil.kt:29)
    	at org.jetbrains.kotlin.ir.declarations.lazy.SynchronizedLazyVar.getValue(lazyUtil.kt:40)
    	at org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass.getInlineClassRepresentation(IrLazyClass.kt:99)
    	at org.jetbrains.kotlin.ir.types.IrTypeSystemContext$DefaultImpls.getUnsubstitutedUnderlyingType(IrTypeSystemContext.kt:451)
    	at org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext.getUnsubstitutedUnderlyingType(JvmIrTypeSystemContext.kt:19)
    	at org.jetbrains.kotlin.ir.types.IrTypeSystemContext$DefaultImpls.getSubstitutedUnderlyingType(IrTypeSystemContext.kt:454)
    	at org.jetbrains.kotlin.backend.jvm.JvmIrTypeSystemContext.getSubstitutedUnderlyingType(JvmIrTypeSystemContext.kt:19)
    	at org.jetbrains.kotlin.backend.jvm.codegen.IrTypeCheckerContextForTypeMapping.getSubstitutedUnderlyingType(IrTypeMapper.kt)
    	at org.jetbrains.kotlin.types.ExpandedTypeUtilsKt.computeExpandedTypeInner(expandedTypeUtils.kt:36)
    	at org.jetbrains.kotlin.types.ExpandedTypeUtilsKt.computeExpandedTypeForInlineClass(expandedTypeUtils.kt:13)
    	at org.jetbrains.kotlin.types.AbstractTypeMapper.mapType(AbstractTypeMapper.kt:104)
    	at org.jetbrains.kotlin.types.AbstractTypeMapper.mapType(AbstractTypeMapper.kt:47)
    	at org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper.mapType(IrTypeMapper.kt:134)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapReturnType(MethodSignatureMapper.kt:180)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapReturnType(MethodSignatureMapper.kt:173)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapSignature(MethodSignatureMapper.kt:263)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapSignature$default(MethodSignatureMapper.kt:228)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapSignatureSkipGeneric(MethodSignatureMapper.kt:223)
    	at org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper.mapAsmMethod(MethodSignatureMapper.kt:57)
    	at org.jetbrains.kotlin.backend.jvm.codegen.IrCodegenUtilsKt.classFileContainsMethod(irCodegenUtils.kt:377)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements.buildReplacement(MemoizedInlineClassReplacements.kt:240)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements.buildReplacement$default(MemoizedInlineClassReplacements.kt:227)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements.createMethodReplacement(MemoizedInlineClassReplacements.kt:183)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements.access$createMethodReplacement(MemoizedInlineClassReplacements.kt:39)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements$getReplacementFunction$1.invoke(MemoizedInlineClassReplacements.kt:85)
    	at org.jetbrains.kotlin.backend.jvm.MemoizedInlineClassReplacements$getReplacementFunction$1.invoke(MemoizedInlineClassReplacements.kt:54)
    	at 
    
    bug help wanted wontfix 
    opened by alexstyl 3
  • Support for a Idiomatic Kotlin API

    Support for a Idiomatic Kotlin API

    There are various opportunities for making developer life easier using the API.

    For starters, a typical way a developer might create a new contact is:

            store.execute(SaveRequest().apply {
                insert(
                    MutableContact().apply {
                        prefix = "A."
                        firstName = "Paolo"
                        middleName = "M."
                        lastName = "Melendez"
                        suffix = "Z."
                    }
                )
            })
    

    This relies on Kotlin's apply extension, which might hint towards embedding the lambda into the API itself. An example:

            store.execute {
                insert(newContact {
                    prefix = "A."
                    firstName = "Paolo"
                    middleName = "M."
                    lastName = "Melendez"
                    suffix = "Z."
                })
            }
    

    In the same fashion, updating a contact could be expressed as:

            store.execute {
                update(contact.mutableCopy {
                    note = Note("To infinity and beyond!")
                })
            }
    

    This is an idea I am still exploring. Unless there is a need/interest for a more idiomatic API, it might be just overengineering.

    opened by alexstyl 3
  • Create static analysis gradle task (detekt + lint)

    Create static analysis gradle task (detekt + lint)

    There are more people contributing to the project lately, and it would be nice to have less moderation required on each PR. This issue is about creating a gradle task to run static analysis in the project.

    Requirements:

    • Introduce detekt in the project (link to Detekt)
    • Create a gradle task called runStaticAnalysis which would run Android lint and detekt.
    • Document is a way (i.e. on your PR) how the static analysis works (such as how to change the rules of each tools).
    • Static analysis should run on all modules (library, library-test, sample) with the same rules.

    Keep in mind that there are going to be way more detekt and lint issues than the default thresholds. There is no need to fix anything in the code that the static analysis will pick up. Instead, increases the threshold of allowed issues to the number of issues detected.

    There is no need for this work to take place in a single PR.

    If you would like to pick up this task, reply to this issue, so that I can assign it to you.

    Link to Detekt: https://detekt.github.io/detekt/ Example Project with static analysis set up: https://github.com/code-with-the-italians/bundel/

    good first issue 
    opened by alexstyl 1
  • 📖 Guest Book

    📖 Guest Book

    This issue is here for you to write any nice things about the project you like. I would love to know how ContactStore has helped you in your projects. If you happen to know any companies/projects that use it, feel free to mention them.

    If you have any issues, questions, suggestions, please open a new issue

    Thanks, Alex

    opened by alexstyl 2
Releases(1.5.0)
  • 1.5.0(Dec 3, 2022)

    Fixes

    • URI formate exception #73
    • Let Android handle encoding when passing user input into ContactStore (7c42bcd6)

    Bumped

    • Compile SDK to 33 (1e2c45e8)
    • Kotlin to 1.7.10 (and related libs)
    • Robelectric to 4.9 (5a27470a)
    Source code(tar.gz)
    Source code(zip)
  • 1.4.0(Jun 21, 2022)

    What's Changed

    • Rename NameLookup to ContactLookup for clarity. You can use ContactLookup to search for any part of a contact (such as their names or phone numbers) https://github.com/alexstyl/contactstore/pull/68
    • Fix a crash when a contact had no lookup key in https://github.com/alexstyl/contactstore/pull/69
    • Added documentation on performance

    Full Changelog: https://github.com/alexstyl/contactstore/compare/1.3.0...1.4.0

    Source code(tar.gz)
    Source code(zip)
  • 1.3.0(Mar 12, 2022)

    What's Changed

    • README typo by @jonapoul in https://github.com/alexstyl/contactstore/pull/59
    • Return lookupKey when fetching from TestContactStore in https://github.com/alexstyl/contactstore/pull/63 (thanks to @itsandreramon for raising this)

    New Contributors

    • @jonapoul made their first contribution in https://github.com/alexstyl/contactstore/pull/59

    Full Changelog: https://github.com/alexstyl/contactstore/compare/1.2.2...1.3.0

    Source code(tar.gz)
    Source code(zip)
  • 1.2.2(Mar 2, 2022)

    Bug fix:

    • Ensures that all ContactQueries String values are non-null #61

    Full Changelog: https://github.com/alexstyl/contactstore/compare/1.2.0...1.2.2

    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Feb 21, 2022)

    What's Changed

    • Remove Coroutines from public API & introduce Coroutines and Reactive modules https://github.com/alexstyl/contactstore/pull/57

    Full Changelog: https://github.com/alexstyl/contactstore/compare/1.0.0...1.2.0

    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Feb 20, 2022)

    What's New

    • No more breaking API changes from this version onward.
    • Allow consumers to specify the account the new contacts will be inserted into (https://github.com/alexstyl/contactstore/pull/54)

    What's Changed

    • Shared Matchers across modules to remove duplication (https://github.com/alexstyl/contactstore/pull/53)
    • Delete Contact instead of RawContact on delete SaveRequest https://github.com/alexstyl/contactstore/pull/55

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.14.1...1.0.0

    Source code(tar.gz)
    Source code(zip)
  • 0.14.1(Feb 19, 2022)

    What's Changed

    • Make all properties of MutableContactBuilder public

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.14.0...0.14.1

    Source code(tar.gz)
    Source code(zip)
  • 0.14.0(Feb 19, 2022)

    Breaking Changes

    • MutableContactBuilder properties are now internal (https://github.com/alexstyl/contactstore/commit/ac8aba502fd6cc02ffafcce0c6c419b275c11baf)
    • LinkedAccountValues Column have been renamed to CustomDataItems. There is now no need to provide any account types, as all custom properties will be returned.
    • ContactLookup now takes a single contact id instead of a list (https://github.com/alexstyl/contactstore/commit/ca57d819f6d8aef97c69629d52334b2b1ede78b9)

    What's New

    • Provide account information in each LabeledValue (https://github.com/alexstyl/contactstore/pull/49)
    • ContactStore will now return empty strings instead of null values where applicable (https://github.com/alexstyl/contactstore/commit/f383904bbe5b5aea42bec0b3aa57ea0e050e3494)
    • ContactPredicates now take plain Strings instead of strongy typed PhoneNumbers or MailAddresses (https://github.com/alexstyl/contactstore/commit/6fd710fb83e1f4331ad008d8625f5668b7c55ca3)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.13.0...0.14.0

    Source code(tar.gz)
    Source code(zip)
  • 0.13.0(Feb 14, 2022)

    What's New

    • Add support for ContactGroups. You can now create new groups, update and delete them. https://github.com/alexstyl/contactstore/pull/48
    • New matcher for comparing groups loosely (d4b3050f)
    • Move all Flows to Dispatchers.IO (3be7e704)
    • Use any kind of Label as custom label value when creating a CP operation https://github.com/alexstyl/contactstore/pull/47
    • MutableContact#contactId is not immutable (973bb1a4)
    • Added MKDocs documentation. See alexstyl.github.io/contactstore
    • Removed org.gradle.jvmargs from gradle.properties (ae07f43e)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.12.0...0.13.0

    Source code(tar.gz)
    Source code(zip)
  • 0.12.0(Feb 2, 2022)

    What's New

    • Made WebAddress raw value to Uri (from String)
    • Renamed Contact#imageUri to Contact#thumbnailUri to make point out the image quality you receive when using it.
    • Removed the _id field of GroupMembership as it is not really required to manage the user's groups.
    • Multiple functions have been deprecated and will be removed in 1.0.0
    • Introduced FormattedStrings.kt. Contains extensions useful for displaying Phone numbers and Postal Addresses to the UI, in a formatted fashion (https://github.com/alexstyl/contactstore/pull/42)
    • Introduced samePropertiesAs() matcher in com.alexstyl:contactstore-test to compare two contacts losely (https://github.com/alexstyl/contactstore/pull/46)

    Bug fixes

    • Fix a crash that was caused by calling Contact#displayName on a MutableContact without all ContactColumns. The columns are now now taken into consideration (https://github.com/alexstyl/contactstore/pull/45)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.11.1...0.12.0

    Source code(tar.gz)
    Source code(zip)
  • 0.11.1(Jan 10, 2022)

    Just a minor bug fix in this one. 🐛

    Corrected the label for LocationHome to return the correct one instead of 'Birthday'

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.11.0...0.11.1

    Source code(tar.gz)
    Source code(zip)
  • 0.11.0(Jan 9, 2022)

    What's New

    • Add builder version of Contact.mutableCopy {} (example)
    • Introduce Label.getLocalizedString() in (example)
    • Introduce DisplayNameStyle in ContactStore#fetchContacts() (example)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.10.0...0.11.0

    Source code(tar.gz)
    Source code(zip)
  • 0.10.0(Jan 7, 2022)

    What's New

    • Added CONTRIBUTING.md (b1a1c2413f9e329699b84c509aafe220f4526868) and CODE_OF_CONDUCT.md (b1a1c2413f9e329699b84c509aafe220f4526868)
    • Run all unit and Android tests on PRs (using Github Actions) by @tatocaster in https://github.com/alexstyl/contactstore/pull/30
    • Introduce shareVCardIntent() functions (31070a28a65a86a66433f64dbee2387220af7884). (example of usage)

    Bug fixes

    • Fix library instrumented tests by @tatocaster in https://github.com/alexstyl/contactstore/pull/27
    • Refresh the sample app by @tatocaster in https://github.com/alexstyl/contactstore/pull/26

    New Contributors

    • @tatocaster made their first contribution in https://github.com/alexstyl/contactstore/pull/26

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.9.0...0.10.0

    Source code(tar.gz)
    Source code(zip)
  • 0.9.0(Jan 1, 2022)

    What's new

    • Add support for SipAddresses (https://github.com/alexstyl/contactstore/commit/a2f9813274477b1c9d95b6ebe52502b84cbb26d3)
    • Add support for Relations (https://github.com/alexstyl/contactstore/commit/03061045337e6cea5a8eeaef7484009f74f149d2)
    • FULL_NAME_STYLE is now only available from Lolipop and above (https://github.com/alexstyl/contactstore/commit/3d753532cd42c6f2cd9beb0a360e05b4f048c5f1)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.8.0...0.9.0

    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Dec 31, 2021)

    What's new

    • Remove Joda Time in favor of the way AOSP parses dates: Any other values are treated as invalid and are ignored.

    Bug Fix

    • Make summary and detail columns optional in AccountInfoResolver: This is done to fix a crash where the values where missing

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.7.1...0.8.0

    Source code(tar.gz)
    Source code(zip)
  • 0.7.1(Dec 29, 2021)

    Bug Fixes

    LookupKey is now a data class instead of inline as a workaround to https://github.com/alexstyl/contactstore/issues/23

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.7.0...0.7.1

    Source code(tar.gz)
    Source code(zip)
  • 0.7.0(Dec 28, 2021)

    New

    • Added the ImAddresses value. It will be populated when a query using the ContactColumn#ImAddresses is used. (https://github.com/alexstyl/contactstore/issues/18)
    • Each Contact fetched from the store, will now have its LookupKey value populated (if exists) (https://github.com/alexstyl/contactstore/issues/22)

    Bug Fixes

    • Fix for ANR. Use trySend and check for channel closure in uriFlow extension by @rbenza in https://github.com/alexstyl/contactstore/pull/20
    • Crash -> Error java.lang.NumberFormatException by @rbenza in https://github.com/alexstyl/contactstore/pull/17

    New Contributors

    • @rbenza made their first contribution in https://github.com/alexstyl/contactstore/pull/20

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.6.0...0.7.0

    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Nov 28, 2021)

    This release includes:

    New

    • Include mimetype in LinkedAccountValue

    The mimetype can be used to differentiate between actions of the same account type (i.e telegram call vs telegram profile)

    Other

    • Use the same version of activity-compose as the rest of the compose dependencies. This is was causing issues with R8 on the sample app. The library should not be affected.
    • Remove jetifier

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.5.0...0.6.0

    Source code(tar.gz)
    Source code(zip)
  • 0.5.0(Nov 24, 2021)

    This release includes:

    New

    • Introduced a DSL in order to simplify the creation, updating and deletion of contacts. (https://github.com/alexstyl/contactstore/commit/b9847749bd626739903763ff1d0ee40a254ba013)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.4.0...0.5.0

    Source code(tar.gz)
    Source code(zip)
  • 0.4.0(Oct 30, 2021)

    This release includes:

    New

    • Simpler API on LinkedAccountValue. data1, data2, etc properties were replaced with summary, detail. New property icon was added. (Thanks to @NLLAPPS for the help parsing contact.xml)
    • Set minSDK to 14 (4.0) (Thanks to @eduardb for the request)
    • Android compile SDK set to 31

    Behavior Changes

    • Remove the IN_VISIBLE check when querying details for contacts. This was done to match the behavior of querying all contacts.
    • Contact#fullNameStyle will only be provided on platforms supporting it (Lollipop and above). In other platforms, the value will always be ContactsContract.FullNameStyle.UNDEFINED.

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.3.3...0.4.0

    Source code(tar.gz)
    Source code(zip)
  • 0.3.3(Oct 24, 2021)

    This release includes:

    New

    • Introduced LinkedAccountValues. This allows to query ContactStore for information synced via 3rd party apps accounts such as Whatsapp, Telegram and so on. You can use this to add 3rd party app integration such as 'Call on Whatsapp' or 'View on Telegram'. (thanks to @NLLAPPS for request & help https://github.com/alexstyl/contactstore/discussions/7)

    Bug Fix

    • Fixed an issue where a Contact's display name and isStarred could be empty when additional information were queried (386edddf)

    Breaking Changes

    • ContactColumn is now a sealed class. You can still get all the supported columns in one go by using the standardColumns() function.
    • TestContactStore was moved to its own package (e3fab6d8)

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.3.1...0.3.3

    Source code(tar.gz)
    Source code(zip)
  • 0.3.1(Oct 10, 2021)

    This release includes:

    New

    • Introduced TestContactStore – An implementation of ContactStore suitable for tests as an additional, optional dependency.

    Full Changelog: https://github.com/alexstyl/contactstore/compare/0.2.0...0.3.1

    Source code(tar.gz)
    Source code(zip)
  • 0.2.0(Oct 2, 2021)

    This release includes:

    New

    • Added Name lookup functionality (ba984538, thanks to @nauhalf & @NLLAPPS for the request & feedback)

    Fixes

    • Clarify behavior of fetchContacts
    • Correct Documentation of Nickname column
    • Remove deprecated kotlinCompilerVersion
    • Rename QueryTest to Lookup
    • Remove unused 'organization' from ContactLookup
    • Create FUNDING.yml
    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Sep 27, 2021)

Owner
Alex Styl
Alex Styl
A modern Wadler/Leijen Prettyprinter

Kotlin port of the Haskell prettyprinter library.

Ben Samuel 2 Dec 14, 2022
Android library which makes it easy to handle the different obstacles while calling an API (Web Service) in Android App.

API Calling Flow API Calling Flow is a Android library which can help you to simplify handling different conditions while calling an API (Web Service)

Rohit Surwase 19 Nov 9, 2021
A logger with a small, extensible API which provides utility on top of Android's normal Log class.

This is a logger with a small, extensible API which provides utility on top of Android's normal Log class. I copy this class into all the little apps

Jake Wharton 9.8k Dec 30, 2022
Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platform the code is running.

Trail Trail is a simple logging system for Java and Android. Create logs using the same API and the library will detect automatically in which platfor

Mauricio Togneri 13 Aug 29, 2022
Consumer android from nutrition-framework API

About This Project (work-in-progress ?? ??️ ??‍♀️ ⛏ ) Consumer Dari Nutrition Framework General Framework for Application Development Around Nutrition

Faisal Amir 0 Feb 24, 2022
Expirable Disk Lru Cache is a secure(with encryption) wrapper for [DiskLruCache](https://github.com/JakeWharton/DiskLruCache) that allows expiring of key/value pairs by specifying evictionTimeSpan. It has very simple API.

ExpirableDiskLruCache ExpirableDiskLruCache is a wrapper for DiskLruCache that allows expiring of key/value pairs by specifying evictionTimeSpan. It h

Vijay Rawat 24 Oct 3, 2022
The REST API backend server for the Jalgaon CoHelp application.

API Service - Jalgaon CoHelp The REST API backend server for the Jalgaon CoHelp application. ?? Technology / Tools used Kotlin: Programming language f

Jalgaon CoHelp 25 Jul 7, 2022
Perfect replacement for startActivityForResult(), based on the Activity Result API.

ActivityResultLauncher English | 中文 Activity Result API is an official tool used to replace the method of startActivityForResult() and onActivityResul

DylanCai 167 Nov 30, 2022
⚙ A beautiful and extensible API for bulding preferences screen

Material Preferences ?? Installation Add this in app's build.gradle file: implementation 'com.imangazaliev.material-prefs:core:<version>' implementati

Mahach Imangazaliev 59 Jul 26, 2022
SSI/NFC desktop/terminal API

Gimly SSI Card Terminal Gimly SSI Card Terminal is a REST API for Self Sovereign Identity interactions between apps, servers, terminals having an NFC

Gimly 0 Nov 8, 2021
Android Shared preference wrapper than encrypts the values of Shared Preferences. It's not bullet proof security but rather a quick win for incrementally making your android app more secure.

Secure-preferences - Deprecated Please use EncryptedSharedPreferences from androidx.security in preferenced to secure-preference. (There are no active

Scott Alexander-Bown 1.5k Dec 24, 2022
Gesture detector framework for multitouch handling on Android, based on Android's ScaleGestureDetector

Android Gesture Detectors Framework Introduction Since I was amazed Android has a ScaleGestureDetector since API level 8 but (still) no such thing as

null 1.1k Nov 30, 2022
Use Android as Rubber Ducky against another Android device

Use Android as Rubber Ducky against another Android device

null 1.4k Jan 9, 2023
Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Android Utilities Library build in kotlin Provide user 100 of pre defined method to create advanced native android app.

Shahid Iqbal 4 Nov 29, 2022
A util for setting status bar style on Android App.

StatusBarUtil A util for setting status bar style on Android App. It can work above API 19(KitKat 4.4). 中文版点我 Sample Download StatusBarUtil-Demo Chang

Jaeger 8.8k Jan 6, 2023
Java implementation of a Disk-based LRU cache which specifically targets Android compatibility.

Disk LRU Cache A cache that uses a bounded amount of space on a filesystem. Each cache entry has a string key and a fixed number of values. Each key m

Jake Wharton 5.7k Dec 31, 2022
a simple cache for android and java

ASimpleCache ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架。轻量到只有一个java文件(由十几个类精简而来)。 1、它可以缓存什么东西? 普通的字符串、JsonObject、JsonArray、Bitmap、Drawable、序列化的java对象,和 b

Michael Yang 3.7k Dec 14, 2022
gRPC and protocol buffers for Android, Kotlin, and Java.

Wire “A man got to have a code!” - Omar Little See the project website for documentation and APIs. As our teams and programs grow, the variety and vol

Square 3.9k Dec 31, 2022
✔️ Secure, simple key-value storage for Android

Hawk 2.0 Secure, simple key-value storage for android Important Note This version has no backward compatibility with Hawk 1+ versions. If you still wa

Orhan Obut 3.9k Dec 20, 2022