Kotlin library for creating long running connections using MQTT protocol

Overview

About Courier

Courier is a kotlin library for creating long running connections using MQTT protocol.

Long running connection is a persistent connection established between client & server for instant bi-directional communication. A long running connection is maintained for maximum possible duration with the help of keep alive packets. This helps in saving battery and data on mobile devices.

MQTT is an extremely lightweight protocol which works on publish/subscribe messaging model. It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth is limited.

The protocol usually runs over TCP/IP; however, any network protocol that provides ordered, lossless, bi-directional connections can support MQTT.

MQTT has 3 built-in QoS levels for Reliable Message Delivery:

  • QoS 0(At most once) - the message is sent only once and the client and broker take no additional steps to acknowledge delivery (fire and forget).

  • QoS 1(At least once) - the message is re-tried by the sender multiple times until acknowledgement is received (acknowledged delivery).

  • QoS 2(Exactly once) - the sender and receiver engage in a two-level handshake to ensure only one copy of the message is received (assured delivery).

Features

  • Clean API

  • Adaptive Keep Alive

  • Message & Stream Adapters

  • Subscription Store

  • Automatic Reconnect & Resubscribe

  • Database Persistence

  • Backpressure handling

  • Alarm, Timer & WorkManager Ping Sender

  • MQTT Chuck

More details about features in Courier library can be found here

Getting Started

Sample App

A demo application is added here which makes Courier connection with a HiveMQ public broker.

Download

Maven Central

All artifacts of Courier library are available via Maven Central.

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.gojek.courier:courier:x.y.z"

    implementation "com.gojek.courier:courier-message-adapter-gson:x.y.z"
    implementation "com.gojek.courier:courier-stream-adapter-rxjava2:x.y.z"
}

Usage

Declare a service interface for actions like Send, Receive, Subscribe, Unsubscribe:

interface MessageService {
	@Receive(topic = "topic/{id}/receive")
	fun receive(@Path("id") identifier: String): Observable<Message>

	@Send(topic = "topic/{id}/send", qos = QoS.TWO)
	fun send(@Path("id") identifier: String, @Data message: Message)

	@Subscribe(topic = "topic/{id}/receive", qos = QoS.ONE)
 	fun subscribe(@Path("id") identifier: String): Observable<Message>

	@Unsubscribe(topics = ["topic/{id}/receive"])
 	fun unsubscribe(@Path("id") identifier: String)
}

Use Courier to create an implementation:

val mqttClient = MqttClientFactory.create(
    context = context,
    mqttConfiguration = MqttV3Configuration(
        authenticator = authenticator
    )
)

val courierConfiguration = Courier.Configuration(
    client = mqttClient,
    streamAdapterFactories = listOf(RxJava2StreamAdapterFactory()),
    messageAdapterFactories = listOf(GsonMessageAdapter.Factory())
)

val courier = Courier(courierConfiguration)

val messageService = courier.create<MessageService>()

Subscribe/Unsubscribe using Service Interface

messageService.subscribe("user-id").subscribe { message ->
    print(message)
}
messageService.unsubscribe("user-id")

Send/Receive using Service Interface

messageService.send("user-id", message)
messageService.receive("user-id") { message ->
    print(message)
}

Connect using MqttClient

val connectOptions = MqttConnectOptions(
    serverUris = listOf(ServerUri(SERVER_URI, SERVER_PORT)),
    clientId = clientId,
    username = username,
    keepAlive = KeepAlive(
        timeSeconds = keepAliveSeconds
    ),
    isCleanSession = cleanSessionFlag,
    password = password
)

mqttClient.connect(connectOptions)

Disconnect using MqttClient

mqttClient.disconnect()

Non-standard Connection options

UserProperties in MqttConnectionOptions

This option allows you to send user-properties in CONNECT packet for MQTT v3.1.1.

val connectOptions = MqttConnectOptions(
    serverUris = listOf(ServerUri(SERVER_URI, SERVER_PORT)),
    clientId = clientId,
    ...
    userPropertiesMap = mapOf(
                "key1" to "value1",
                "key2" to "value2"
    )
)

mqttClient.connect(connectOptions)

⚠️ ** This is a non-standard option. As far as the MQTT specification is concerned, user-properties support is added in MQTT v5. So to support this in MQTT v3.1.1, broker needs to have support for this as well.

Contribution Guidelines

Read our contribution guide to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Courier Android library.

License

All Courier modules except Paho are MIT Licensed. Paho is Eclipse Licensed.

Comments
  • TooManyRequestException in Library

    TooManyRequestException in Library

    Caused by android.net.ConnectivityManager$TooManyRequestsException android.net.ConnectivityManager.convertServiceException (ConnectivityManager.java:3609)

    android.net.ConnectivityManager.registerDefaultNetworkCallback (ConnectivityManager.java:4256) com.gojek.networktracker.NetworkStateTrackerImpl.startTracking$network_tracker_release (NetworkStateTrackerImpl.java:115) com.gojek.networktracker.NetworkStateTrackerImpl.addListener (NetworkStateTrackerImpl.java:86) com.gojek.keepalive.OptimalKeepAliveCalculator. (OptimalKeepAliveCalculator.java:31) com.gojek.keepalive.KeepAliveCalculatorFactory.create (KeepAliveCalculatorFactory.java:29) com.gojek.keepalive.OptimalKeepAliveProvider. (OptimalKeepAliveProvider.java:16) com.gojek.keepalive.OptimalKeepAliveProvider. (OptimalKeepAliveProvider.java:9) com.gojek.mqtt.client.internal.MqttClientInternal.initialiseOptimalKeepAliveProvider (MqttClientInternal.java:157) com.gojek.mqtt.client.internal.MqttClientInternal.initialiseAdaptiveMqttClient (MqttClientInternal.java:122) com.gojek.mqtt.client.internal.MqttClientInternal. (MqttClientInternal.java:64)

    opened by sulemankhan447 4
  • Add handling for subscription not ack by broker

    Add handling for subscription not ack by broker

    When the client is trying to subscribe to topics if the broker is not able to subscribe successfully we get a SUBACK from broker with grantedQos of 128. In this case, currently we still get MqttSubscribeSuccessEvent form the library leading to false positives.

    The change is to provide a failure in this case which leads to retrying the subscription request internally and once the MAX retry is exhausted provide a MqttSubscribeFailureEvent to the client and keep retrying internally.

    This handles the scenario where the broker has not successfully subscribed and the same is communicated to the client via the library.

    https://github.com/gojek/courier-android/issues/37

    opened by rubenquadros12 4
  • No event for SubAck 128 return code

    No event for SubAck 128 return code

    When subscribing for a topic if at the broker end there is a timeout the broker gives a SubAck with granted Qos as 128. This event is never propagated to the consumer by the courier lib.

    opened by rubenquadros 1
  • Handled crash when subscribing to invalid topic

    Handled crash when subscribing to invalid topic

    Fix for https://github.com/gojek/courier-android/issues/32#issue-1306745655

    There is a crash when we try to connect to an invalid topic. For example home# or home+

    Steps to reproduce -:

    1. Connect to a MQTT broker
    2. Subscribe to an invalid topic name. Example home#

    Error Logs -:

    2022-07-16 13:45:37.778 10608-10647/com.gojek.courier.app E/AndroidRuntime: FATAL EXCEPTION: MQTT_Thread
        Process: com.gojek.courier.app, PID: 10608
        java.lang.IllegalArgumentException: Invalid usage of multi-level wildcard in topic string: home#
            at org.eclipse.paho.client.mqttv3.MqttTopic.validate(MqttTopic.java:200)
            at org.eclipse.paho.client.mqttv3.MqttAsyncClient.subscribe(MqttAsyncClient.java:889)
            at com.gojek.mqtt.connection.MqttConnection.subscribe(MqttConnection.kt:474)
            at com.gojek.mqtt.client.v3.impl.AndroidMqttClient.subscribeMqtt(AndroidMqttClient.kt:465)
            at com.gojek.mqtt.scheduler.runnable.SubscribeRunnable.run(SubscribeRunnable.kt:11)
            at android.os.Handler.handleCallback(Handler.java:938)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loopOnce(Looper.java:201)
            at android.os.Looper.loop(Looper.java:288)
            at android.os.HandlerThread.run(HandlerThread.java:67)
    

    Video for your reference

    opened by hiteshchopra11 1
  • Crash when subscribing to an invalid topic

    Crash when subscribing to an invalid topic

    There is a crash when we try to connect to an invalid topic. For example home# or home+

    Steps to reproduce -:

    1. Connect to a MQTT broker
    2. Subscribe to an invalid topic name. Example home#

    Error Logs -:

    2022-07-16 13:45:37.778 10608-10647/com.gojek.courier.app E/AndroidRuntime: FATAL EXCEPTION: MQTT_Thread
        Process: com.gojek.courier.app, PID: 10608
        java.lang.IllegalArgumentException: Invalid usage of multi-level wildcard in topic string: home#
            at org.eclipse.paho.client.mqttv3.MqttTopic.validate(MqttTopic.java:200)
            at org.eclipse.paho.client.mqttv3.MqttAsyncClient.subscribe(MqttAsyncClient.java:889)
            at com.gojek.mqtt.connection.MqttConnection.subscribe(MqttConnection.kt:474)
            at com.gojek.mqtt.client.v3.impl.AndroidMqttClient.subscribeMqtt(AndroidMqttClient.kt:465)
            at com.gojek.mqtt.scheduler.runnable.SubscribeRunnable.run(SubscribeRunnable.kt:11)
            at android.os.Handler.handleCallback(Handler.java:938)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loopOnce(Looper.java:201)
            at android.os.Looper.loop(Looper.java:288)
            at android.os.HandlerThread.run(HandlerThread.java:67)
    

    Video for your reference

    opened by hiteshchopra11 1
  • Compatibility for Android 13 alarm restrictions

    Compatibility for Android 13 alarm restrictions

    Thanks for this much needed android library now that paho project is abandoned.

    With Android 13, setExactAndAllowWhileIdle() permission is now protected permission & only alarm apps can use it.

    Can the connection with broker reliably survive in background without this permission? What are your plans to target Android 13 compatibility.

    TIA

    opened by ishaangarg 1
  • Connect to AWS IoT with custom authentication over SSL or TLS

    Connect to AWS IoT with custom authentication over SSL or TLS

    I am trying to connect to AWS IoT MQTT broker using courier library with custom authentication. It gets connected via WSS scheme but not with SSL or TLS scheme.

    opened by HariPrasanth 0
  • Create new implementation of PersistableSubscriptionStore

    Create new implementation of PersistableSubscriptionStore

    This implementation[PersistableSubscriptionStoreV2] allows the client to subscribe topics irrespective of the currently subscribed topics, maintained by the subscription store.

    In the other implementation[PersistableSubscriptionStore], subscription requests are ignored when the topics are already subscribed.

    opened by deepanshu42 0
  • Can not receive from topic that have wildcard

    Can not receive from topic that have wildcard

    @Receive(topic = “{topic}“)
    fun receive(@Path(“topic”) topic: String): Observable<String>
    

    when the topic is "some/word/+/foo" , it can not receive message from "some/word/1234/foo" . i think this happen because courier try to listen topic "some/word/+/foo" with actual '+', not wildcard.

    but, from interceptor, courier successfully receive the message from the topic.

    opened by khalif0898 0
  • Add new implementation of RunnableScheduler which stops the thread on destroy

    Add new implementation of RunnableScheduler which stops the thread on destroy

    #31 Create a new implementation of IRunnableScheduler which stops the thread when destroy api of MqttClient is invoked. The thread is started again when connect api is invoked.

    To use this new implementation, pass the config isRunnableSchedulerV2Enabled as true inside ExperimentConfigs

    opened by deepanshu42 0
  • `HandlerThread` is never stopped

    `HandlerThread` is never stopped

    If we want to connect to courier for a short duration and then dispose it, since the HandlerThread is never stopped it is always active till the app is killed.

    opened by rubenquadros12 0
  • Compose migration improvement

    Compose migration improvement

    • Update colors for Dark and Light Palette
    • Add the missing search functionality in Transaction Detail Page using Compose
    • Introduce navigation-compose library and use it to navigate from TransactionListScreen to TransactionDetailScreen, removing the need for keeping the TransactionDetailActivity and TransactionDetailFragment classes (both are deleted in this PR)
    opened by rizkyfadillah 0
  • Use inline/value class instead of primitive type on the data class properties declaration

    Use inline/value class instead of primitive type on the data class properties declaration

    As we use primitive type on the data class properties declaration it would give a high chance for error-prone of doing substitution in between properties. Indeed naturally we're able to avoid this issue by declaring the name of the properties themselves when assigning the value. However, it still has a chance the users do not follow the convention for doing that. The consideration for using the inline/value class can be helpful here.

    opened by radityagumay 0
Releases(0.1.6)
  • 0.1.6(Dec 2, 2022)

    What's Changed

    • Fix alpn empty exception for android N and below by @anubhav7nov in https://github.com/gojek/courier-android/pull/53

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.5...0.1.6

    Source code(tar.gz)
    Source code(zip)
  • 0.1.5(Nov 18, 2022)

    What's Changed

    • Fix argument injection in topic name by @deepanshu42 in https://github.com/gojek/courier-android/pull/52

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.4...0.1.5

    Source code(tar.gz)
    Source code(zip)
  • 0.1.4(Nov 14, 2022)

    What's Changed

    • Remove built-in adapters and add text message adapter by @deepanshu42 in https://github.com/gojek/courier-android/pull/51

    Breaking Changes

    • Built-in message & stream adapters are removed. If using them, consider using TextMessageAdapterFactory or creating your own custom implementations.

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.3...0.1.4

    Source code(tar.gz)
    Source code(zip)
  • 0.1.3(Oct 31, 2022)

    What's Changed

    • Add topic & content-type in message adapter by @deepanshu42 in https://github.com/gojek/courier-android/pull/48
    • Update documentation by @deepanshu42 in https://github.com/gojek/courier-android/pull/50

    Note: This is a breaking change if you are creating your own custom message adapters. Refer this for the new implementation.

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.2...0.1.3

    Source code(tar.gz)
    Source code(zip)
  • 0.1.2(Oct 28, 2022)

    What's Changed

    • Update com.google.code.gson dependency by @sebasrock in https://github.com/gojek/courier-android/pull/47
    • Remove empty username check by @deepanshu42 in https://github.com/gojek/courier-android/pull/49

    New Contributors

    • @sebasrock made their first contribution in https://github.com/gojek/courier-android/pull/47

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.1...0.1.2

    Source code(tar.gz)
    Source code(zip)
  • 0.1.1(Sep 28, 2022)

    What's Changed

    • removed java 11 dependency by @anubhav7nov in https://github.com/gojek/courier-android/pull/45

    Full Changelog: https://github.com/gojek/courier-android/compare/0.1.0...0.1.1

    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Sep 28, 2022)

    What's Changed

    • Improved TLS handling and add support for ALPN by @anubhav7nov in https://github.com/gojek/courier-android/pull/43

    New Features

    Note: Set shouldUseNewSSLFlow to true to enable these features

    Breaking Change

    • Change how you create MqttConnectOptions

    Before

     val connectOptions = MqttConnectOptions(
                serverUris = listOf(ServerUri(SERVER_URI, SERVER_PORT)),
                clientId = clientId,
                username = username,
                keepAlive = KeepAlive(
                    timeSeconds = keepAliveSeconds
                ),
                isCleanSession = cleanSessionFlag,
                password = password
            )
    

    After

    
     val connectOptions = MqttConnectOptions.Builder()
                  .serverUris(listof(ServerUri(SERVER_URI, SERVER_PORT)))
                  .clientId(clientId)
                  .userName(username)
                  .password(password)
                  .keepAlive(KeepAlive(timeSeconds = keepAliveSeconds))
                  .cleanSession(cleanSessionFlag)
                  .build()
    
    • Removed socket factory from MqttV3Configuration. You should now set socketFactory and sslSocketFactory in MqttConnectOptions.

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.9...0.1.0

    Source code(tar.gz)
    Source code(zip)
  • 0.0.9(Sep 13, 2022)

    What's Changed

    • Update logic for finding optimal keepalive by @deepanshu42 in https://github.com/gojek/courier-android/pull/41

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.8...0.0.9

    Source code(tar.gz)
    Source code(zip)
  • 0.0.8(Sep 1, 2022)

    What's Changed

    • Update readme with badges by @anubhav7nov in https://github.com/gojek/courier-android/pull/39
    • Handled crash when subscribing to invalid topic by @hiteshchopra11 in https://github.com/gojek/courier-android/pull/33
    • Add handling for subscription not ack by broker by @rubenquadros12 in https://github.com/gojek/courier-android/pull/38
    • Remove keep alive validation when current KA failure limit is exceeded by @deepanshu42 in https://github.com/gojek/courier-android/pull/40

    New Contributors

    • @hiteshchopra11 made their first contribution in https://github.com/gojek/courier-android/pull/33
    • @rubenquadros12 made their first contribution in https://github.com/gojek/courier-android/pull/38

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.7...0.0.8

    Source code(tar.gz)
    Source code(zip)
  • 0.0.7(Jul 25, 2022)

    What's Changed

    • Remove androidx proguard exclusion rule by @anubhav7nov in https://github.com/gojek/courier-android/pull/36

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.6...0.0.7

    Source code(tar.gz)
    Source code(zip)
  • 0.0.6(Jul 20, 2022)

    What's Changed

    • Add workaround for room-db compilation failure on M1 chips by @deepanshu42 in https://github.com/gojek/courier-android/pull/28
    • Updated publishMavenLocal script and add steps in Contribution.md by @anubhav7nov in https://github.com/gojek/courier-android/pull/29
    • Create new implementation of PersistableSubscriptionStore by @deepanshu42 in https://github.com/gojek/courier-android/pull/34

    Breaking Change

    • Replaced isPersistentSubscriptionStoreEnabled flag with SubscriptionStore. If you were passing the flag as false then you can start passing the subscriptionStore as IN_MEMORY now.

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.5...0.0.6

    Source code(tar.gz)
    Source code(zip)
  • 0.0.5(Jun 23, 2022)

    What's Changed

    • Upgrade dependency version for AndroidX Lifecycle Extensions by @anubhav7nov in https://github.com/gojek/courier-android/pull/19
    • Add release.yml for publishing GH pages documentation by @deepanshu42 in https://github.com/gojek/courier-android/pull/23
    • Update documentation footer by @deepanshu42 in https://github.com/gojek/courier-android/pull/24
    • Update home banner by @deepanshu42 in https://github.com/gojek/courier-android/pull/26
    • Add workmanager-2.6.0-pingsender compatible with compileSdkVersion < 31 by @deepanshu42 in https://github.com/gojek/courier-android/pull/27

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.4...0.0.5

    Source code(tar.gz)
    Source code(zip)
  • 0.0.4(Jun 2, 2022)

    What's Changed

    • Added spotless and detekt for code quality by @anubhav7nov in https://github.com/gojek/courier-android/pull/18

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.3...0.0.4

    Source code(tar.gz)
    Source code(zip)
  • 0.0.3(May 27, 2022)

    What's Changed

    • Add UserProperties in CONNECT packet for MQTT v3.1.1 by @anubhav7nov in https://github.com/gojek/courier-android/pull/13
    • Integrate Docusauras by @deepanshu42 in https://github.com/gojek/courier-android/pull/12
    • Add courier logos by @deepanshu42 in https://github.com/gojek/courier-android/pull/14
    • Update logos by @deepanshu42 in https://github.com/gojek/courier-android/pull/15
    • Update documentation by @deepanshu42 in https://github.com/gojek/courier-android/pull/16
    • Add support for global message listener by @deepanshu42 in https://github.com/gojek/courier-android/pull/17

    Full Changelog: https://github.com/gojek/courier-android/compare/0.0.2...0.0.3

    Source code(tar.gz)
    Source code(zip)
  • 0.0.2(May 9, 2022)

Owner
Gojek
SuperApp from Southeast Asia
Gojek
Esp touch flutter plugin - Client-side (mobile) Android Flutter implementation for ESP-Touch protocol

esp_touch_flutter_plugin Client-side (mobile) Android Flutter implementation for

huangyanxiong 0 Jan 21, 2022
The Action helps you to send a message to a queue on a RabbitMQ running Server

Rabbit Sender Action This Action helps you to send a message to a queue on a RabbitMQ running Server. Inputs Arg Default Description RABBIT_USERNAME g

Quique Ferraris 12 Dec 1, 2022
Repository of a multi-platform application running the same Compose source code on all platforms

Compose multiplatform demo demo.mov Using the same compose user interface (UI) from android on all principal platforms ?? ?? App Features This is a si

David Coronel 18 Dec 16, 2022
An example of a test task for creating a simple currency converter application for the Android platform. The app is developed using Kotlin, MVI, Dagger Hilt, Retrofit, Jetpack Compose.

Simple Currency Converter Simple Currency Converter Android App by Isaev Semyon An example of a test task for creating a simple currency converter app

Semyon Isaev 1 Nov 8, 2021
A library for creating dynamic skeleton view

Skeleton Placeholder View Overview A Library designed to draw a Skeleton by "skinning" the view from a provided layout. Skeleton is composed of Bone w

Ferry Irawan 25 Jul 20, 2021
Framework for quickly creating connected applications in Kotlin with minimal effort

Ktor is an asynchronous framework for creating microservices, web applications and more. Written in Kotlin from the ground up. import io.ktor.server.n

ktor.io 10.7k Jan 9, 2023
Carousel Recyclerview let's you create carousel layout with the power of recyclerview by creating custom layout manager.

Carousel Recyclerview Create carousel effect in recyclerview with the CarouselRecyclerview in a simple way. Including in your project Gradle Add below

Jack and phantom 514 Jan 8, 2023
Example mod with Mixin to help you to get started with creating a mod with mixins.

ExampleMixinMod Example mod with Mixin to help you to get started with creating a mod with mixins. For usage of mixins, see here. Also, remember to tu

null 0 Dec 16, 2021
An android application for creating a journal for subjects you studied and also you can set timer for break.

Study Journal An android application for creating a journal for subjects you studied and also you can set timer for break between two consecutive subj

Prasoon 3 Aug 10, 2022
Survey-service - Application for creating online surveys and polls

Survey Service Application for creating online surveys and polls Functionality A

Anatoly Babushkin 2 Apr 6, 2022
Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties

Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties Idea Delegated properties in Kotlin allow you to execute a

null 25 Dec 27, 2022
Kotlinx-murmurhash - Kotlin Multiplatform (KMP) library for hashing using MurmurHash

kotlinx-murmurhash Kotlin Multiplatform (KMP) library for MurmurHash, a non-cryp

Gonçalo Silva 23 Dec 27, 2022
A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisation and okio

Store A tiny Kotlin multiplatform library that assists in saving and restoring objects to and from disk using kotlinx.coroutines, kotlinx.serialisatio

Isuru Rajapakse 98 Jan 3, 2023
Notes-App-Kotlin - Notes App Built Using Kotlin

Notes-App-Kotlin Splash Screen Home Page Adding New Notes Filter Feature Search

Priyanka 4 Oct 2, 2022
A Kotlin Native program to show the time since a date, using Kotlin LibUI

TimeSince A Kotlin Native program to show the time since a date, using Kotlin LibUI Report Bug . Request Feature About The Project TimeSince is a Kotl

Russell Banks 2 May 6, 2022
RoomJetpackCompose is an app written in Kotlin and shows a simple solution to perform CRUD operations in the Room database using Kotlin Flow in clean architecture.

RoomJetpackCompose is an app written in Kotlin and shows a simple solution to perform CRUD operations in the Room database using Kotlin Flow in clean architecture.

Alex 27 Jan 1, 2023
Maildroid is a small robust android library for sending emails using SMTP server

Maildroid ?? Maildroid is a small robust android library for sending emails using SMTP server ?? Key Features • Add to your project • Documentation •

Nedim 174 Dec 22, 2022
With Viola android face detection library, you can detect faces in a bitmap, crop faces using predefined algorithm and get additional information from the detected faces.

Viola Viola android face detection library detects faces automatically from a bitmap, crop faces using the predefined algorithms, and provides supplem

Darwin Francis 58 Nov 1, 2022
Library App - Using Android studio / Final project

Library-App Library App - Using Android studio / Final project Screens SplashScreen: it’s launcher activity will be moved to MainActivity auto after 2

Baseel 3 Feb 2, 2022