app module, shared core libraries, and self-contained features. The UI layer is built entirely with Jetpack Compose following the MVVM pattern. Protocol-level logic — including end-to-end encryption, API communication, and session management — is delegated to Kalium, a Kotlin Multiplatform library included as a Git submodule.
Module structure
app
Entry point of the Android application. Contains
WireApplication, all screen
composables, ViewModels, navigation graph definition, and the top-level DI setup.core/*
Reusable modules consumed by both
app and features. Each module has a
single responsibility (analytics, media, navigation wrappers, notifications,
shared UI components).features/*
Independently developed feature modules (cells, meetings, sketch, sync).
Each module owns its own UI, ViewModels, and navigation destinations.
kalium
Kotlin Multiplatform library providing all protocol logic: MLS and Proteus
encryption, API client, session management, and use-case definitions.
Clean architecture layers
Wire Android follows clean architecture with a strict dependency direction:UI layer — Jetpack Compose
All screens are
@Composable functions. They observe StateFlows exposed by
their corresponding ViewModel and dispatch events via method calls or
Channels. No business logic lives in composables.ViewModel layer
Each screen has a Hilt-injected
ViewModel (or a @ViewModelScoped variant
for sub-screens). ViewModels call use-case lambdas/functions provided by
Kalium and transform results into UI state.UseCase layer — Kalium
Kalium exposes plain Kotlin callable objects (use cases) through scoped
containers (
GlobalScope, SessionScope, sub-scopes such as
ConversationScope, CallsScope). They encapsulate all business and
protocol logic.The Android codebase never imports Kalium repository or data-source
classes directly. All access goes through the use-case objects vended by
CoreLogic.getGlobalScope() or CoreLogic.getSessionScope(userId).Kalium submodule
Kalium is included underkalium/ as a Git submodule. After cloning the
repository you must initialise it:
- MLS (Messaging Layer Security) and Proteus end-to-end encryption
- Wire API client (Ktor-based)
CoreLogic— the single entry point used by the Android appGlobalScope— session-independent use cases (login, account list, server config)SessionScope— per-user use cases (messaging, calls, files, sync)
MVVM with Jetpack Compose
Each screen follows this pattern:AssistedInject for parameterised ViewModels
ViewModels that require runtime arguments (e.g. a specificConversationsSource
or a search query Flow) use Hilt’s AssistedInject. This avoids passing data
through LaunchedEffect calls and makes each ViewModel dedicated to exactly one
configuration, which simplifies testing and debugging (see ADR-0005).
Calling architecture
Calling uses two separate Activities to isolate disposable incoming/outgoing call UI from the long-lived ongoing call UI (ADR-0002):| Activity | Purpose |
|---|---|
StartingActivity | Handles incoming and outgoing call UI. Disposable — recreated for each new call. |
OngoingCallActivity | Handles the ongoing call. Kept alive for the full call duration. |
Build system
build-logic — convention plugins
Reusable Gradle plugins live inbuild-logic/plugins/. Modules apply them by
id instead of copy-pasting configuration:
| Plugin | Effect |
|---|---|
wire.android.library | Android library defaults (compile SDK, Java 17, etc.) |
wire.android.application | Android application defaults + APK renaming |
wire.hilt | Applies dagger.hilt.android.plugin + com.google.devtools.ksp, adds Hilt/KSP dependencies |
wire.android.navigation | Adds core:navigation + Compose Destinations + KSP |
wire.kmp.library | Kotlin Multiplatform library defaults |
buildSrc — build-time constants
buildSrc/ provides Kotlin objects consumed during configuration time:
BuildTypes— debug / release build type namesDependencies— shared dependency version strings (before the version catalog covers everything)flavor/— app flavour definitions (Dev, Staging, Internal, Beta, Prod, F-Droid)- Custom Gradle tasks (
IncludeGitBuildTask,WriteKeyValuesToFileTask,RenameApkTask)
KSP for annotation processing
All annotation processing — Hilt code generation and Compose Destinations destination/nav-graph generation — uses KSP (Kotlin Symbol Processing) rather than KAPT, giving faster incremental builds.App flavours
Dev (red)
Bleeding-edge builds targeting the Wire Staging backend. All experimental
features enabled. Logging on.
Staging (orange)
Release-like builds for QA against the Staging backend. Includes extra dev
tools. Logging on.
Beta (blue)
Internal dogfood builds targeting Wire Prod. Some pre-release features.
Logging on.
Prod / F-Droid (white)
Public production builds. Logging is disabled by default and never uploaded.
F-Droid variant contains no closed-source dependencies.