Releases: adityaa-codes/echo-android
v1.1.0
v1.1.0 — Protocol Correctness
Package: io.github.adityaa-codes:echo:1.1.0
Minimum SDK: Android 30 (Android 11)
Kotlin: 2.3.10 · Ktor: 3.4.0
🎉 What's New
The v1.1.0 release focuses on maximizing stability and protocol correctness with the Pusher Protocol (v7). This update improves how the SDK handles long-lived connections, expiring authentication tokens, and granular server-side error states, ensuring a smooth experience when working with robust backends like Laravel Reverb, Soketi, and Sockudo.
✨ Features & Improvements
1. Per-Channel Error Handling
ChannelState.Failed: Channels now expose a discreteFailed(error: EchoError)state in theirStateFlow<ChannelState>if their specific subscription request fails (e.g., 403 Forbidden on a single private channel).- Per-channel Error Stream: A new
channel.error: StateFlow<EchoError?>property allows UIs to render granular error states without relying purely on the global client error stream. - Protocol Support: The SDK now intercepts and acts upon
pusher_internal:subscription_errorframes.
2. Terminal & Degraded Connection States
The ConnectionState hierarchy has been extended to stop infinite and destructive reconnection loops.
ConnectionState.Suspended: The SDK will now enter a degraded "Suspended" state after a configurable threshold of continuous reconnection failures (suspendAfterMs, defaulting to 2 minutes). Retries will slow down significantly instead of blindly looping.ConnectionState.Failed: If the server rejects a connection with an unrecoverable Pusher close code (codes 4001–4009, such as an Invalid App Key), the client will gracefully halt all retry attempts and enter a terminalFailedstate.
3. Proactive Token Refresh
- Zero-Downtime Auth Renewals: The
auth { }DSL block now accepts atokenExpiryMshint. The SDK will automatically launch a proactive refresh timer in the background, firing off theAuthenticatorand seamlessly re-sending thepusher:subscribeframe onto the existing socket before the token expires, preventing socket tear-downs on long-lived dashboard sessions.
4. Live Presence Updates
pusher_internal:member_updated: The SDK now fully supports and integrates the member updated frame sent by Laravel Reverb (≥1.4) and Soketi.updating {}Callback: A new chainable callback has been added toPresenceChannel. You can now receive live metadata changes (e.g., status changes or typing flags) for a member without them needing to drop and re-join the connection.
5. Project & Internal Polish
- Project Structure Reorganization: The primary logic module was renamed from
:coreto:echoto better match the final published artifact, and internal documentation was consolidated under thedocs/folder. - Local Testing Ecosystem: Instructions for spinning up the companion Laravel DDEV backend (
echo-server) locally have been added to the README to streamline SDK integration testing.
📦 Upgrading
Update your dependency in build.gradle.kts:
implementation("io.github.adityaa-codes:echo:1.1.0")Note: If you previously monitored the global StateFlow<ConnectionState> and mapped it exhaustively (e.g., using a when block without an else), you will need to add branches for the new Suspended and Failed sealed subtypes to ensure your project continues compiling successfully.
v1.0.0
Release Notes
v1.0.0 — Initial Stable Release
Package: io.github.adityaa-codes:echo:1.0.0
Minimum SDK: Android 30 (Android 11)
Kotlin: 2.3.10 · Ktor: 3.4.0
🎉 What's New
This is the first stable release of the Echo Kotlin SDK — a robust, type-safe, and idiomatic Kotlin client for Pusher-compatible WebSocket services, with first-class support for Laravel Reverb.
✨ Features
Core Client
- Kotlin DSL configuration via
Echo.create { ... }— configure host, API key, port, TLS, and auth in a single builder block. EchoClientinterface exposesconnect(),disconnect(),ping(),channel(),private(),presence(), andleave().StateFlow<ConnectionState>— observeDisconnected,Connecting,Connected, andReconnectingstates reactively.SharedFlow<EchoError>— global typed error stream for centralized error handling.Flow<EchoEvent>— global event stream for all incoming WebSocket frames.socketIdproperty available when connected.activeChannelslist for inspecting currently subscribed channels.
Connection Lifecycle & Resilience
- Mutex-guarded connection state machine preventing concurrent connect/disconnect races.
- Protocol-level ping/pong keep-alive — automatic response to
pusher:pingwith configurable 30 s pong timeout. - Automatic reconnection with configurable exponential backoff and jitter.
- Reconnection DSL — tune
maxAttempts,baseDelayMs, andmaxDelayMsdirectly in the builder. - Connection timeout for fast-fail on unreachable servers.
Channel Support
- Public channels — subscribe with
echo.channel("name")and listen for any event. - Private channels — subscribe with
echo.private("name");private-prefix added automatically; authenticated before subscribe. - Presence channels — subscribe with
echo.presence("name");presence-prefix added automatically; supportshere {},joining {}, andleaving {}callbacks with full member tracking. - Client whispers on private channels via
whisper(event, data)with enforcedclient-prefix. - Channel state machine —
Unsubscribed,Subscribing,Subscribed,Failedper-channel state exposed asStateFlow<ChannelState>. - Subscription deduplication — repeated
channel()/private()/presence()calls return the existing instance. - Auto-resubscribe — all active channels are automatically re-authenticated and re-subscribed upon reconnection.
Protocol & Serialization
- Pusher Protocol v7 compliant — full handling of
pusher:connection_established,pusher:ping,pusher:pong,pusher:error,pusher_internal:subscription_succeeded,pusher_internal:member_added,pusher_internal:member_removed. - Polymorphic serialization using
kotlinx.serializationsealed class hierarchies with@SerialName— no manual JSON parsing. ignoreUnknownKeys = truefor forward compatibility with future server frames.
Authentication
- Suspending
Authenticatorinterface — integrate with any auth backend (HTTP, token-based, custom). - Pre-flight auth — authentication is completed before sending
pusher:subscribefor private/presence channels. - Typed auth failures —
EchoError.Auth(status, body)emitted on failure without tearing down the connection. onAuthFailurecallback in the DSL for token refresh hooks.
Pluggable Architecture
EchoEngineinterface — swap the underlying WebSocket transport. Default:KtorEchoEngine(Ktor + OkHttp).EchoSerializerinterface — provide a custom frame serializer/deserializer.- Both are configurable directly in the
client { }DSL block.
Error Handling
- Sealed
EchoErrorhierarchy:EchoError.Network— network/IO failures with cause.EchoError.Auth— authentication failures with HTTP status and body.EchoError.Protocol— Pusher protocol errors (codes 4000–4299).EchoError.Serialization— JSON parsing failures with cause.
Developer Experience
internalby default — minimal public API footprint; only consumer-facing types arepublic.- Explicit API mode (
explicitApi()) enabled to prevent accidental API surface leaks. - Structured concurrency throughout — no
GlobalScope;CoroutineScopeis injected. - Configurable logging — enable debug logs or supply a custom logger via
logging { }DSL block. - KDoc on all public API members with usage samples.
📦 Installation
// build.gradle.kts
dependencies {
implementation("io.github.adityaa-codes:echo:1.0.0")
}🔧 Quick Start
val echo = Echo.create {
client {
host = "your-reverb-server.com"
apiKey = "your-app-key"
port = 8080
useTls = false
}
auth {
authenticator = { channelName, socketId ->
Result.success("""{"auth":"$socketId:signature"}""")
}
}
reconnection {
maxAttempts = 10
baseDelayMs = 1_000
maxDelayMs = 30_000
}
logging { enabled = true }
}
echo.connect()
val channel = echo.channel("chat-room")
channel.listen("MessageSent") { event -> println(event.data) }
echo.state.collect { state -> println("Connection: $state") }
echo.errors.collect { error -> println("Error: $error") }📋 Requirements
| Requirement | Version |
|---|---|
| Android minSdk | 30 (Android 11) |
| compileSdk | 36 (Android 16) |
| Kotlin | 2.3.10 |
| Java | 11+ |
| Gradle | 9.1+ |
🧪 Test Coverage
- 40 unit tests across 8 test classes.
- ≥ 80% line coverage on the core library module.
- Tests written with MockK and app.cash.turbine for Flow assertions.
- Integration tests use Ktor MockEngine for end-to-end protocol verification.
📁 Sample App
A fully functional reference app is included in the sample module demonstrating connection lifecycle, channel subscriptions, presence tracking, error handling, and manual ping — all built with UDF architecture (StateFlow<ViewState> + processIntent()).
./gradlew :sample:installDebug