Signed-off-by: ale <ale@manalejandro.com>
AleJabber — XMPP/Jabber Client for Android
A modern, feature-rich XMPP/Jabber messaging client for Android built with Jetpack Compose and Material Design 3. AleJabber supports multiple accounts, end-to-end encryption (OTR, OMEMO, OpenPGP), multimedia file transfers via http_upload, in-app audio recording, group chat rooms, and full accessibility support.
Table of Contents
- Features
- Screenshots
- Architecture
- Project Structure
- Getting Started
- Configuration
- Encryption
- Multimedia & Audio
- Internationalization
- Accessibility
- Dependencies
- Contributing
- License
Features
| Feature | Description |
|---|---|
| 🔐 OTR Encryption | Off-the-Record messaging with perfect forward secrecy |
| 🔒 OMEMO Encryption | XEP-0384 multi-device end-to-end encryption (recommended) |
| 🗝️ OpenPGP | Asymmetric PGP encryption via XEP-0373/0374 |
| 👥 Multi-Account | Manage multiple XMPP accounts from different servers |
| 💬 Group Rooms (MUC) | Join and manage Multi-User Chat rooms (XEP-0045) |
| 📎 File Transfer | Upload images, audio, and files via XEP-0363 http_upload |
| 🎙️ Audio Messages | Record and send voice messages directly from the app |
| 🔔 Smart Notifications | Per-account notification channels with vibration/sound control |
| 🌐 Multilingual | English 🇬🇧, Spanish 🇪🇸, Chinese 🇨🇳 |
| ♿ Accessible | Full TalkBack support with content descriptions and semantic roles |
| 🎨 Material You | Dynamic theming with Light/Dark/System modes |
| 🔄 Auto-Reconnect | Automatic reconnection with random increasing delay policy |
| 💾 Offline Storage | Room database caches messages for offline reading |
Screenshots
Screenshots to be added after first device deployment.
Architecture
AleJabber follows Clean Architecture with an MVVM presentation layer:
┌─────────────────────────────────────────────────────┐
│ Presentation Layer │
│ Compose Screens ←→ ViewModels ←→ UI State │
├─────────────────────────────────────────────────────┤
│ Domain Layer │
│ Models · Use Cases · Repository Interfaces │
├─────────────────────────────────────────────────────┤
│ Data Layer │
│ Room DB · Smack XMPP · DataStore · OkHttp │
└─────────────────────────────────────────────────────┘
Key Patterns
- Dependency Injection: Hilt (Dagger-based)
- Reactive Streams: Kotlin
Flow+StateFlow+SharedFlow - Navigation: Jetpack Navigation Compose with type-safe routes
- Background Work:
XmppForegroundServicekeeps connections alive - Persistence: Room with KSP-generated DAOs
Project Structure
app/src/main/java/com/manalejandro/alejabber/
├── AleJabberApp.kt # Application class with Hilt initialization
├── MainActivity.kt # Single-Activity entry point
│
├── data/
│ ├── local/
│ │ ├── AppDatabase.kt # Room database definition
│ │ ├── dao/ # Data Access Objects (AccountDao, MessageDao, ContactDao, RoomDao)
│ │ └── entity/ # Room entities (AccountEntity, MessageEntity, etc.)
│ ├── remote/
│ │ └── XmppConnectionManager.kt # Smack connection lifecycle manager
│ └── repository/
│ ├── AccountRepository.kt
│ ├── ContactRepository.kt
│ ├── MessageRepository.kt
│ └── RoomRepository.kt
│
├── domain/
│ └── model/ # Pure Kotlin domain models
│ ├── Account.kt # XMPP account model
│ ├── Contact.kt # Roster contact
│ ├── Message.kt # Chat message with encryption + media metadata
│ ├── Room.kt # MUC room
│ └── Enums.kt # EncryptionType, MessageStatus, PresenceStatus, etc.
│
├── di/
│ └── AppModule.kt # Hilt module: DB, OkHttp, DataStore, XmppManager
│
├── media/
│ ├── AudioRecorder.kt # MediaRecorder wrapper with StateFlow
│ └── HttpUploadManager.kt # XEP-0363 file upload via OkHttp
│
├── service/
│ ├── XmppForegroundService.kt # Foreground service keeping XMPP alive
│ └── BootReceiver.kt # BroadcastReceiver to restart on boot
│
└── ui/
├── theme/
│ ├── Color.kt # Brand colors + bubble colors
│ ├── Theme.kt # Material3 dynamic theme with AppTheme enum
│ └── Type.kt # Typography scale
├── navigation/
│ ├── Screen.kt # Sealed class route definitions
│ └── AleJabberNavGraph.kt # NavHost with all destinations
├── components/
│ ├── AvatarWithStatus.kt # Avatar + presence dot component
│ └── EncryptionBadge.kt # Encryption type indicator badge
├── accounts/
│ ├── AccountsScreen.kt # Account list with connect/disconnect
│ ├── AccountsViewModel.kt
│ └── AddEditAccountScreen.kt # Add/edit XMPP account form
├── contacts/
│ ├── ContactsScreen.kt # Roster list with search + presence
│ └── ContactsViewModel.kt
├── rooms/
│ ├── RoomsScreen.kt # MUC rooms list
│ └── RoomsViewModel.kt
├── chat/
│ ├── ChatScreen.kt # Full chat UI with bubbles, media, recording
│ └── ChatViewModel.kt
└── settings/
├── SettingsScreen.kt # App preferences
└── SettingsViewModel.kt
Getting Started
Prerequisites
- Android Studio Hedgehog (2023.1.1) or later
- JDK 11+
- Android SDK 36
- A running XMPP server (e.g., ejabberd, Prosody, Openfire)
Build
# Clone the repository
git clone https://github.com/manalejandro/AleJabber.git
cd AleJabber
# Build debug APK
./gradlew assembleDebug
# Install on connected device
./gradlew installDebug
# Run unit tests
./gradlew test
# Run instrumented tests
./gradlew connectedAndroidTest
The debug APK will be at:
app/build/outputs/apk/debug/app-debug.apk
Configuration
Adding an XMPP Account
- Open the app → tap Accounts tab
- Press the + FAB
- Fill in:
- JID — your full Jabber ID, e.g.
user@jabber.org - Password — your account password
- Server (optional) — override DNS-resolved hostname
- Port (default: 5222) — custom port if needed
- TLS — toggle to require TLS (recommended)
- Resource (default: AleJabber) — client resource identifier
- JID — your full Jabber ID, e.g.
Gradle Properties
gradle.properties contains build-time flags:
| Property | Default | Description |
|---|---|---|
android.disallowKotlinSourceSets |
false |
Required for KSP + AGP 9.x compatibility |
org.gradle.jvmargs |
-Xmx2048m |
Gradle daemon heap size |
android.useAndroidX |
true |
AndroidX migration flag |
Encryption
AleJabber supports three levels of end-to-end encryption, selectable per conversation:
OMEMO (Recommended — XEP-0384)
- Multi-device, forward-secrecy encryption based on the Signal Protocol
- Works even when the recipient is offline
- Select OMEMO in the encryption picker (🔒 icon in chat toolbar)
OTR (Off-the-Record — XEP-0364)
- Classic two-party encryption with perfect forward secrecy
- Requires both parties to be online simultaneously
- Best for high-privacy one-on-one conversations
OpenPGP (XEP-0373/0374)
- Asymmetric RSA/ECC encryption using PGP key pairs
- Works offline; keys must be exchanged in advance
- Uses Bouncy Castle (
bcpg-jdk18on,bcprov-jdk18on)
None (Plain Text)
- Messages are sent unencrypted over the TLS-secured XMPP stream
- Only use on trusted, private servers
Multimedia & Audio
File Upload (XEP-0363 http_upload)
- Tap the 📎 attach button in the chat input
- Select any file from the device storage
- AleJabber requests an upload slot from the XMPP server
- The file is PUT to the provided URL via OkHttp
- The download URL is sent as a message body
- Images are auto-rendered inline in the chat bubble
Audio Messages
- In the chat input, press and hold the 🎙️ microphone button
- Speak your message — a recording timer appears
- Release to send, or tap ✕ to cancel
- Audio is recorded with
MediaRecorder(AAC/MP4 format) - The recording is uploaded via
http_uploadautomatically
Note: Microphone permission (
RECORD_AUDIO) is requested on first use.
Internationalization
AleJabber ships with three locale bundles:
| Locale | File |
|---|---|
| English (default) | app/src/main/res/values/strings.xml |
| Spanish | app/src/main/res/values-es/strings.xml |
| Chinese (Simplified) | app/src/main/res/values-zh/strings.xml |
To add a new language:
- Create
app/src/main/res/values-<locale>/strings.xml - Copy all keys from the default
strings.xml - Translate each string value
Accessibility
AleJabber is designed to be fully usable with Android's TalkBack screen reader:
- All interactive elements have
contentDescriptionlabels - Message status icons (sent, delivered, read) announce their state
- Recording timer is announced to screen readers
- Encryption badge announces the current encryption type
- Avatar components announce contact name and presence status
- The app passes the Accessibility Scanner basic checks
Dependencies
| Library | Version | Purpose |
|---|---|---|
| Jetpack Compose BOM | 2024.09.00 | UI framework |
| Material3 | (via BOM) | Design system |
| Hilt | 2.59.2 | Dependency injection |
| Room | 2.7.0 | Local database |
| Navigation Compose | 2.9.0 | In-app navigation |
| Smack (XMPP) | 4.4.8 | XMPP protocol implementation |
| OkHttp | 4.12.0 | HTTP file uploads |
| Coil | 2.7.0 | Image loading |
| DataStore | 1.1.1 | Settings persistence |
| Accompanist Permissions | 0.36.0 | Runtime permission handling |
| Bouncy Castle | 1.78.1 | OpenPGP crypto |
| Coroutines | 1.9.0 | Async/reactive programming |
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch:
git checkout -b feature/my-feature - Commit your changes:
git commit -m "feat: add my feature" - Push to the branch:
git push origin feature/my-feature - Open a Pull Request
Code Style
- Follow Kotlin coding conventions
- Use
ktlintfor formatting:./gradlew ktlintCheck - Write KDoc comments for all public functions and classes
- Add unit tests for ViewModels and Repository classes
License
MIT License
Copyright (c) 2026 Manuel Alejandro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.