Thank you for your interest in contributing! This document provides guidelines and information for contributors.
- Code of Conduct
- Getting Started
- Development Setup
- Code Style
- Commit Conventions
- Pull Request Process
- Reporting Issues
- Architecture Guidelines
This project follows the Contributor Covenant Code of Conduct. By participating, you are expected to uphold this code. Please report unacceptable behavior by opening an issue.
- Fork the repository on GitHub
- Clone your fork locally:
git clone https://github.com/<your-username>/echo-android.git cd echo-android
- Create a branch for your work:
git checkout -b feature/your-feature-name
- Android Studio Ladybug (2024.2+) or newer
- JDK 11+
- Android SDK with API 36 installed
# Build the library
./gradlew :core:assemble
# Build everything (library + sample app)
./gradlew assemble# Unit tests
./gradlew :core:testDebugUnitTest
# Unit tests with coverage report
./gradlew :core:createDebugUnitTestCoverageReport
# View report at: core/build/reports/coverage/test/debug/index.html
# All tests (core + sample)
./gradlew testDebugUnitTestThe project uses ktlint for Kotlin code style enforcement.
# Check for violations
./gradlew :core:ktlintCheck :sample:ktlintCheck
# Auto-format violations
./gradlew :core:ktlintFormat :sample:ktlintFormat- Kotlin only — no Java source files
- Immutability — prefer
valovervar; usecopy()for state updates - Visibility — default to
internal; only usepublicfor intended consumer API - Trailing commas — mandatory for multi-line parameter lists and collections
- Expression bodies — use
= ...for single-expression functions - No
GlobalScope— always use structured concurrency
| Type | Convention | Example |
|---|---|---|
| Classes/Interfaces | PascalCase |
EchoClient, EchoEngine |
| Functions/Properties | camelCase |
connect(), socketId |
| Constants | UPPER_SNAKE_CASE |
DEFAULT_PORT |
| Backing properties | _camelCase |
_connectionState |
- Clean Architecture —
domainmust not depend ondataorpresentation - No DTOs in presentation — always map to domain models at the repository boundary
implementationonly — never useapi(...)in Gradle unless a type is part of the public API surface- Serialization — use
kotlinx.serializationexclusively (no Gson/Moshi/Jackson)
All code must pass ktlintCheck before merge. The project enforces the Kotlin coding conventions style. Run ./gradlew ktlintFormat to auto-fix most issues.
This project follows Conventional Commits.
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
| Type | Description |
|---|---|
feat |
A new feature |
fix |
A bug fix |
docs |
Documentation changes only |
style |
Formatting, missing semicolons, etc. (no code change) |
refactor |
Code change that neither fixes a bug nor adds a feature |
perf |
Performance improvement |
test |
Adding or correcting tests |
build |
Changes to build system or dependencies |
ci |
Changes to CI configuration |
chore |
Maintenance tasks |
sdk— core library changessample— sample app changesdeps— dependency updatesdocs— documentation
feat(sdk): add presence channel member tracking
fix(sdk): resolve reconnection race condition on API 30
docs: update README with presence channel usage
build(deps): upgrade Ktor to 3.4.1
test(sdk): add EchoEngine edge case tests
-
Ensure your branch is up-to-date with
main/master:git fetch origin git rebase origin/master
-
Run all checks locally before pushing:
./gradlew :core:assemble :core:testDebugUnitTest :core:ktlintCheck
-
Open a Pull Request with:
- A clear title following commit conventions
- A description of what changed and why
- Links to any related issues
-
Review criteria:
- All CI checks pass (build, tests, lint)
- Code coverage does not decrease below 80%
- No new
api(...)dependencies without justification - Public API changes are intentional and documented
- KDoc is present on all new public APIs
-
After approval, the maintainer will squash-merge your PR.
Please include:
- Device / emulator and Android API level
- Echo SDK version (or commit hash)
- Steps to reproduce the issue
- Expected behavior vs. actual behavior
- Logs / stack traces if available
Please include:
- Use case — what problem are you trying to solve?
- Proposed solution — how would you like it to work?
- Alternatives considered — any other approaches you've thought of?
Before contributing new features, please understand the project architecture:
core/
├── Echo.kt # Entry point & DSL builder
├── EchoClient.kt # Public client interface
├── auth/ # Authenticator contracts
├── channel/ # Channel interfaces
├── connection/ # Connection state machine & reconnection
├── data/protocol/ # Pusher wire protocol models
├── engine/ # Pluggable WebSocket engine
├── error/ # Typed error hierarchy
├── internal/ # All implementations (internal visibility)
├── serialization/ # Pluggable serializer
├── state/ # ConnectionState & ChannelState
└── utils/ # Logger utilities
- Interfaces live in public packages (
channel/,engine/,auth/, etc.) - Implementations live in
internal/withinternalvisibility - State is exposed as
StateFlow— never mutable state directly - Errors flow through
SharedFlow<EchoError>— never thrown - Engine and Serializer are pluggable — consumers can replace defaults
If you have questions about contributing, feel free to open an issue with the question label.
Thank you for helping make the Echo Kotlin SDK better! 🎉