| Two-way Chat | Two-way Chat | Empty Chat |
![]() |
![]() |
![]() |
Download & install the APK file to try the app.
- SDK level 32 to 36 API
- Written in Kotlin
- MVVM design pattern with Clean architecture principles.
- Dependency injection with Hilt
- Safe API call with Retrofit & Coroutines
- Caching API response with OkHttpClient
- Observing data changes and updating the UI state with StateFlow
- Jetpack Compose in one of the module with Material 3 UI
- Room Database
- Jetpack libraries
- Navigation - Handling navigation between destinations within the app
- Lifecycle - Handling lifecycles with lifecycle-aware component
- ViewModel - Storing and managing UI-related data in a lifecycle-conscious way
- Kotlin Coroutines
- Kotlin Flow
- OkHttp - Implementing interceptors
- Certificate Transparency : For API Security
- Truth (Fluent assertions for Java and Android)
- Junit (Unit tests)
- Turbine (A small testing library for kotlinx.coroutines Flow)
- MockK (mocking library for Kotlin)
Decision:
Implement Clean Architecture with strict separation between layers.
Rationale:
- Ensures testability, scalability, and maintainability.
- Enables mocking and independent testing of each layer.
- Aligns with Android Architecture Guidelines and SOLID principles.
Implementation:
- Domain Layer β Pure Kotlin business logic, no Android dependencies.
- Data Layer β Room database, DAOs, and repository implementations.
- Presentation Layer β Jetpack Compose UI + ViewModel-driven state.
- Dependency Inversion β Interfaces define behavior; concrete implementations are injected via Hilt.
Decision:
Separate UI state (ChatUIState) from the ViewModel, exposing it as a StateFlow.
Rationale:
- Improves UI testability and enables stateless Composables.
- Simplifies recomposition and reduces side effects.
- Allows unit testing of UI logic without rendering Compose UI.
Implementation:
- ChatListViewModel holds a single immutable
StateFlow<ChatUIState>. - Composable functions observe state updates using
collectAsState(). onMessageTextChanged()andonUserSelected()trigger updates safely usinglaunchSafe().
Decision:
Group messages based on sender ID and time proximity.
Rationale:
- Reduces visual clutter and improves readability.
- Mimics established UX patterns from modern messaging apps.
- Prevents redundant timestamp headers and groups contextually related messages.
Implementation:
- Consecutive messages from the same sender within 20 seconds are grouped.
- Messages separated by β₯1 hour show a timestamp header.
- Implemented via
groupMessagesBySenderAndTime()in the domain layer β ensuring pure, testable logic.
Decision:
Use an isRead boolean flag on each message, with corresponding UI indicators.
Rationale:
- Lightweight and effective approach for message read tracking.
- Mirrors familiar UX from messaging apps (β for sent, ββ for read).
- Easy to extend if backend synchronization is added later.
Visual Indicators:
- ββ (Gray color): Sent
- ββ (Yellow color): Read
Implementation:
markMessagesAsRead(currentUserId)in DAO updates unread messages.- ViewModel automatically triggers read updates on user switch.
Decision:
Use Room Database with LocalDateTime for message timestamps.
Rationale:
- Room simplifies data persistence with compile-time query validation.
LocalDateTimeprovides robust time operations and formatting.- Custom
TypeConvertersensure proper serialization/deserialization.
Implementation:
MessagesDaoandUsersDaodefine reactive CRUD operations returning Flows.- Tested using in-memory Room database for integration coverage.
- Verified that insert, update, and
markMessagesAsRead()behave correctly.
Decision:
Use two predefined local users with switchable roles.
Rationale:
- Simplifies the demo without implementing authentication or network APIs.
- Keeps focus on core chat functionality.
- Demonstrates local persistence and real-time state updates.
Implementation:
- Users defined as constants: Sara (user1) and Alex (user2).
- Stored locally via
UserEntityin Room. - Switching users triggers automatic read-status updates.
Decision:
Implement a comprehensive multi-layer testing strategy using industry-standard tools.
Rationale:
- Guarantees each layer behaves correctly in isolation and integration.
- Promotes confidence in refactoring, CI/CD automation, and scalability.
- Validates both synchronous and asynchronous logic (Flows, coroutines, and state).
androidTest/β Instrumentation tests using Espresso and Hilt Test libraries.debug/β ContainsHiltTestActivity, used as a host for UI tests.test-utils/β Shared utilities module for both unit and instrumentation tests (e.g., common data, custom Hilt Test Runner).test/β Unit tests with MockK, and Hilt.
- Java 21+
- Android Studio Otter | 2025.2.1
- AGP 8.0+
Assumption: Only two predefined users exist in the system.
Justification:
- Keeps the architecture simple while demonstrating user switching.
- Focuses on core messaging functionality instead of authentication.
- Reflects realistic chat context between two local profiles β Sara and Alex.
Assumption: Messages are delivered instantly and marked as read when the other user is active.
Justification:
- Simulates real-time delivery behavior without introducing networking or server logic.
- Simplifies testing and ensures deterministic results in UI and repository tests.
- Keeps user experience consistent and predictable for demo purposes.
Assumption: Uses local Room database only β no remote backend or synchronization.
Justification:
- Emphasizes offline-first design and clean data flow architecture.
- Demonstrates repository abstraction and Flow-based reactive persistence.
- Reduces complexity, focusing the project on clean architecture, not networking.
Assumption: Only text messages are supported.
Justification:
- Maintains a focused scope to demonstrate message persistence, grouping, and display.
- Avoids complications like media storage or compression logic.
- Ensures testing remains lightweight and business-ruleβcentric.
Assumption: Uses a standardized relative time format β
βTodayβ, βYesterdayβ, or a formatted date string.
Justification:
- Aligns with modern messaging app conventions (e.g., WhatsApp, Telegram).
- Enhances readability and user familiarity.
- Keeps logic centralized in LocalDateTime extension functions β making it easily testable.
Assumption: Based on Muzz brand colors and Material 3 Design principles.
Justification:
- Creates a professional and modern appearance.
- Ensures accessibility with contrast-friendly colors and adaptive theming.
- Separates state-driven UI logic for better composability and testability.
Assumption: Designed for moderate message volumes (hundreds, not thousands).
Justification:
- Ideal for small-scale, demo, or personal messaging apps.
- Ensures smooth scrolling and lightweight Room database operations.
- Focuses on architectural clarity over large-scale optimization.
Assumption: All tests run on JVM or instrumented devices using mock or in-memory data.
Justification:
- Eliminates external dependencies for repeatable, isolated test results.
- Guarantees full coverage across domain, data, and presentation layers.
- Aligns with the Clean Architecture goal of testability and modularity.
This demo project focuses on Clean Architecture, local data flow, and UI/UX patterns β not on full-scale production chat functionality.
Below are the current known limitations and potential future improvements:
| π§© Area | π« Limitation | π Future Improvement |
|---|---|---|
| Networking | No network layer β all messages are stored locally using Room | Add WebSocket or REST API for real-time message sync and cloud persistence |
| Conversations | Single hardcoded conversation (e.g., βSaraβ, "Alex") | Implement conversation list with multiple user threads |
| Media Support | Only text messages supported | Add media picker for images, audio, and file attachments |
| Animations | Basic send/receive transitions only | Introduce smoother message bubble animations and micro-interactions |
| Read Receipts | Missing visual checkmarks for sent/read states | Add isRead flag with β (sent) and ββ (read) indicators |
| Typing Indicators | Not implemented | Add βUser is typingβ¦β indicators using Flow or socket events |
| Error Handling | Limited β errors logged but not displayed to user | Add user-friendly error messages and retry actions |
| Pagination | Loads all messages at once | Add pagination or lazy loading for large message histories |
| User Switching | Two predefined users only (Sara & Alex) | Extend to dynamic user accounts with authentication flow |
| Dark and Light Theme | Supported Light Theme at the moment but already done setup for dark theme as well | Extend to dynamic theme selection |



